Initial commit: Animexe Laravel platform
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,334 @@
|
||||
@extends('admin.layouts.app')
|
||||
@section('title', 'Anime Ekle')
|
||||
@section('page-title', 'Anime Ekle')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.cover-preview {
|
||||
width:100%; aspect-ratio:2/3; object-fit:cover;
|
||||
border-radius:var(--radius-sm); display:block;
|
||||
}
|
||||
.cover-placeholder {
|
||||
width:100%; aspect-ratio:2/3;
|
||||
background:var(--surface);
|
||||
border:2px dashed var(--border2);
|
||||
border-radius:var(--radius-sm);
|
||||
display:flex; flex-direction:column;
|
||||
align-items:center; justify-content:center;
|
||||
gap:8px; color:var(--text3); font-size:13px;
|
||||
cursor:pointer; transition:border-color .15s, background .15s;
|
||||
}
|
||||
.cover-placeholder:hover { border-color:var(--accent); background:var(--accent-dim); color:var(--accent-lt); }
|
||||
.banner-placeholder {
|
||||
width:100%; aspect-ratio:16/5;
|
||||
background:var(--surface);
|
||||
border:2px dashed var(--border2);
|
||||
border-radius:var(--radius-sm);
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
gap:8px; color:var(--text3); font-size:13px;
|
||||
cursor:pointer; transition:border-color .15s, background .15s;
|
||||
}
|
||||
.banner-placeholder:hover { border-color:var(--accent); background:var(--accent-dim); color:var(--accent-lt); }
|
||||
.section-card {
|
||||
background:var(--bg2);
|
||||
border:1px solid var(--border);
|
||||
border-radius:var(--radius);
|
||||
margin-bottom:16px;
|
||||
overflow:hidden;
|
||||
}
|
||||
.section-header {
|
||||
display:flex; align-items:center; gap:10px;
|
||||
padding:14px 20px;
|
||||
border-bottom:1px solid var(--border);
|
||||
}
|
||||
.section-header h6 {
|
||||
font-size:13.5px; font-weight:700;
|
||||
color:var(--text); margin:0;
|
||||
}
|
||||
.section-body { padding:20px; }
|
||||
.genre-chip {
|
||||
display:inline-flex; align-items:center; gap:6px;
|
||||
padding:5px 12px; border-radius:99px;
|
||||
border:1px solid var(--border2);
|
||||
background:transparent;
|
||||
color:var(--text2); font-size:12.5px; font-weight:600;
|
||||
cursor:pointer; transition:all .15s; user-select:none;
|
||||
}
|
||||
.genre-chip input { display:none; }
|
||||
.genre-chip:has(input:checked) {
|
||||
background:var(--accent-dim);
|
||||
border-color:var(--accent);
|
||||
color:var(--accent-lt);
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
|
||||
{{-- Breadcrumb --}}
|
||||
<nav class="mb-4">
|
||||
<a href="{{ route('admin.animes.index') }}" class="btn btn-secondary btn-sm">
|
||||
<i class="bi bi-arrow-left"></i> Animelere Dön
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<form method="POST" action="{{ route('admin.animes.store') }}" enctype="multipart/form-data" id="anime-form">
|
||||
@csrf
|
||||
|
||||
<div class="row g-4">
|
||||
|
||||
{{-- ── Sol: Form Alanları ── --}}
|
||||
<div class="col-xl-8 col-lg-7">
|
||||
|
||||
{{-- Temel Bilgiler --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-info-circle" style="color:var(--accent-lt)"></i>
|
||||
<h6>Temel Bilgiler</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Türkçe Başlık <span style="color:var(--danger)">*</span></label>
|
||||
<input type="text" name="title" class="form-control form-control-lg" value="{{ old('title') }}" placeholder="Anime adını girin..." required style="font-size:15px;font-weight:600">
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">İngilizce Başlık</label>
|
||||
<input type="text" name="title_en" class="form-control" value="{{ old('title_en') }}" placeholder="English title...">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Japonca Başlık</label>
|
||||
<input type="text" name="title_jp" class="form-control" value="{{ old('title_jp') }}" placeholder="日本語タイトル...">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Açıklama</label>
|
||||
<textarea name="description" class="form-control" rows="5" placeholder="Anime hakkında kısa açıklama...">{{ old('description') }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Detaylar --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-sliders" style="color:var(--info)"></i>
|
||||
<h6>Detaylar</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Tip <span style="color:var(--danger)">*</span></label>
|
||||
<select name="type" class="form-select" required>
|
||||
<option value="series" {{ old('type')=='series' ?'selected':'' }}>Dizi (Series)</option>
|
||||
<option value="movie" {{ old('type')=='movie' ?'selected':'' }}>Film (Movie)</option>
|
||||
<option value="ova" {{ old('type')=='ova' ?'selected':'' }}>OVA</option>
|
||||
<option value="ona" {{ old('type')=='ona' ?'selected':'' }}>ONA</option>
|
||||
<option value="special" {{ old('type')=='special' ?'selected':'' }}>Special</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Durum <span style="color:var(--danger)">*</span></label>
|
||||
<select name="status" class="form-select" required>
|
||||
<option value="ongoing" {{ old('status','ongoing')=='ongoing' ?'selected':'' }}>Devam Ediyor</option>
|
||||
<option value="completed" {{ old('status')=='completed' ?'selected':'' }}>Tamamlandı</option>
|
||||
<option value="upcoming" {{ old('status')=='upcoming' ?'selected':'' }}>Yakında</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Yayın Yılı</label>
|
||||
<input type="number" name="release_year" class="form-control" value="{{ old('release_year', date('Y')) }}" min="1900" max="2099">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Stüdyo</label>
|
||||
<input type="text" name="studio" class="form-control" value="{{ old('studio') }}" placeholder="Örn: MAPPA, WIT Studio...">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">MyAnimeList ID</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-link-45deg"></i></span>
|
||||
<input type="number" name="mal_id" class="form-control" value="{{ old('mal_id') }}" placeholder="Örn: 16498">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Fragman URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-youtube" style="color:#ff0000"></i></span>
|
||||
<input type="url" name="trailer_url" class="form-control" value="{{ old('trailer_url') }}" placeholder="https://youtube.com/watch?v=...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Türler --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-tags" style="color:var(--warning)"></i>
|
||||
<h6>Türler</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
@foreach($genres as $genre)
|
||||
<label class="genre-chip">
|
||||
<input type="checkbox" name="genres[]" value="{{ $genre->id }}"
|
||||
{{ in_array($genre->id, old('genres', [])) ? 'checked' : '' }}>
|
||||
{{ $genre->name }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- İzin Ayarları --}}
|
||||
@if(isset($permissions) && count($permissions))
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-shield-lock" style="color:var(--success)"></i>
|
||||
<h6>İzin Ayarları</h6>
|
||||
<span style="font-size:12px;color:var(--text2);margin-left:4px">— Bu anime için özel izinler</span>
|
||||
</div>
|
||||
<div class="section-body p-0">
|
||||
@foreach($permissions as $perm)
|
||||
<div class="perm-row">
|
||||
<div>
|
||||
<div class="perm-name">{{ $perm->label }}</div>
|
||||
@if($perm->description)
|
||||
<div class="perm-desc">{{ $perm->description }}</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="perm-toggle">
|
||||
<input type="radio" name="permissions[{{ $perm->key }}]"
|
||||
id="perm_{{ $perm->key }}_free" value="free" checked>
|
||||
<label for="perm_{{ $perm->key }}_free" class="free">Ücretsiz</label>
|
||||
<input type="radio" name="permissions[{{ $perm->key }}]"
|
||||
id="perm_{{ $perm->key }}_prem" value="premium">
|
||||
<label for="perm_{{ $perm->key }}_prem" class="premium">Premium</label>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
{{-- ── Sağ: Görsel & Ayarlar ── --}}
|
||||
<div class="col-xl-4 col-lg-5">
|
||||
|
||||
{{-- Kapak Görseli --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-image" style="color:var(--info)"></i>
|
||||
<h6>Kapak Görseli</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<input type="file" name="cover_image" id="cover-input" accept="image/*"
|
||||
style="display:none" onchange="previewCover(this)">
|
||||
<div onclick="document.getElementById('cover-input').click()" style="cursor:pointer">
|
||||
<img id="cover-preview" class="cover-preview" src="" alt="" style="display:none">
|
||||
<div class="cover-placeholder" id="cover-ph">
|
||||
<i class="bi bi-cloud-upload" style="font-size:26px"></i>
|
||||
<span>Kapak yükle</span>
|
||||
<span style="font-size:11px;color:var(--text3)">Önerilen: 300×450px</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Banner Görseli --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-panorama" style="color:var(--info)"></i>
|
||||
<h6>Banner Görseli</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<input type="file" name="banner_image" id="banner-input" accept="image/*"
|
||||
style="display:none" onchange="previewBanner(this)">
|
||||
<div onclick="document.getElementById('banner-input').click()" style="cursor:pointer">
|
||||
<img id="banner-preview" style="width:100%;aspect-ratio:16/5;object-fit:cover;border-radius:var(--radius-sm);display:none" alt="">
|
||||
<div class="banner-placeholder" id="banner-ph">
|
||||
<i class="bi bi-cloud-upload" style="font-size:22px"></i>
|
||||
<span>Banner yükle</span>
|
||||
<span style="font-size:11px;color:var(--text3)">Önerilen: 1280×720px</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Yayın Ayarları --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-toggle-on" style="color:var(--success)"></i>
|
||||
<h6>Yayın Ayarları</h6>
|
||||
</div>
|
||||
<div class="section-body d-flex flex-column gap-3">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<div style="font-size:13.5px;font-weight:600;color:var(--text)">Yayınla</div>
|
||||
<div style="font-size:12px;color:var(--text2)">Kullanıcılara görünür yap</div>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" name="is_published" value="1"
|
||||
id="is_published" role="switch">
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<div style="font-size:13.5px;font-weight:600;color:var(--text)">Öne Çıkar</div>
|
||||
<div style="font-size:12px;color:var(--text2)">Ana sayfada öne çıkar</div>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" name="is_featured" value="1"
|
||||
id="is_featured" role="switch">
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<div style="font-size:13.5px;font-weight:600;color:var(--text)">Türkçe Dublaj</div>
|
||||
<div style="font-size:12px;color:var(--text2)">Ana sayfada Dublaj bölümünde göster</div>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" name="is_dubbed" value="1"
|
||||
id="is_dubbed" role="switch">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Kaydet --}}
|
||||
<button type="submit" class="btn btn-primary w-100 btn-lg">
|
||||
<i class="bi bi-plus-circle"></i> Anime Ekle
|
||||
</button>
|
||||
<a href="{{ route('admin.animes.index') }}" class="btn btn-secondary w-100 mt-2">
|
||||
İptal
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function previewCover(input) {
|
||||
if (!input.files[0]) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
document.getElementById('cover-preview').src = e.target.result;
|
||||
document.getElementById('cover-preview').style.display = 'block';
|
||||
document.getElementById('cover-ph').style.display = 'none';
|
||||
};
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
}
|
||||
function previewBanner(input) {
|
||||
if (!input.files[0]) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
document.getElementById('banner-preview').src = e.target.result;
|
||||
document.getElementById('banner-preview').style.display = 'block';
|
||||
document.getElementById('banner-ph').style.display = 'none';
|
||||
};
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,369 @@
|
||||
@extends('admin.layouts.app')
|
||||
@section('title', 'Düzenle: ' . $anime->title)
|
||||
@section('page-title', $anime->title)
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.section-card { background:var(--bg2); border:1px solid var(--border); border-radius:var(--radius); margin-bottom:16px; overflow:hidden; }
|
||||
.section-header { display:flex; align-items:center; gap:10px; padding:14px 20px; border-bottom:1px solid var(--border); }
|
||||
.section-header h6 { font-size:13.5px; font-weight:700; color:var(--text); margin:0; }
|
||||
.section-body { padding:20px; }
|
||||
.ai-banner {
|
||||
display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:12px;
|
||||
padding:14px 18px;
|
||||
background:linear-gradient(135deg,rgba(6,182,212,0.06),rgba(124,58,237,0.06));
|
||||
border:1px solid rgba(6,182,212,0.2);
|
||||
border-radius:var(--radius-sm);
|
||||
margin-bottom:16px;
|
||||
}
|
||||
.genre-chip { display:inline-flex; align-items:center; gap:6px; padding:5px 12px; border-radius:99px; border:1px solid var(--border2); background:transparent; color:var(--text2); font-size:12.5px; font-weight:600; cursor:pointer; transition:all .15s; user-select:none; }
|
||||
.genre-chip input { display:none; }
|
||||
.genre-chip:has(input:checked) { background:var(--accent-dim); border-color:var(--accent); color:var(--accent-lt); }
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="d-flex align-items-center gap-3 mb-4 flex-wrap">
|
||||
<a href="{{ route('admin.animes.show', $anime) }}" class="btn btn-secondary btn-sm">
|
||||
<i class="bi bi-arrow-left"></i> Geri Dön
|
||||
</a>
|
||||
<div style="font-size:13px;color:var(--text2)">
|
||||
Son güncelleme: {{ $anime->updated_at->format('d.m.Y H:i') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- AI Banner --}}
|
||||
<div class="ai-banner">
|
||||
<div>
|
||||
<div style="font-size:13.5px;font-weight:700;color:var(--info);display:flex;align-items:center;gap:7px">
|
||||
<i class="bi bi-stars"></i> AI ile Tüm Boşları Doldur
|
||||
</div>
|
||||
<div style="font-size:12px;color:var(--text2);margin-top:2px">
|
||||
Açıklama, yıl, stüdyo, tür, durum ve puan bilgilerini DeepSeek'ten çeker. Yalnızca boş alanları doldurur.
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-info btn-sm" id="ai-meta-btn" onclick="aiAutoFill()">
|
||||
<i class="bi bi-magic"></i> Tümünü Doldur
|
||||
</button>
|
||||
</div>
|
||||
<div id="ai-meta-status" style="display:none;font-size:13px;padding:8px 12px;border-radius:8px;margin-bottom:12px"></div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.animes.update', $anime) }}" enctype="multipart/form-data">
|
||||
@csrf @method('PUT')
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-xl-8 col-lg-7">
|
||||
|
||||
{{-- Temel Bilgiler --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-info-circle" style="color:var(--accent-lt)"></i>
|
||||
<h6>Temel Bilgiler</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Türkçe Başlık <span style="color:var(--danger)">*</span></label>
|
||||
<input type="text" name="title" class="form-control" value="{{ old('title', $anime->title) }}" required style="font-size:15px;font-weight:600">
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">İngilizce Başlık</label>
|
||||
<input type="text" name="title_en" class="form-control" value="{{ old('title_en', $anime->title_en) }}">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Japonca Başlık</label>
|
||||
<input type="text" name="title_jp" class="form-control" value="{{ old('title_jp', $anime->title_jp) }}">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label class="form-label mb-0">Açıklama</label>
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="ai-anime-btn" onclick="aiGenerateAnime()">
|
||||
<i class="bi bi-stars" style="color:var(--info)"></i> AI ile Yaz
|
||||
</button>
|
||||
</div>
|
||||
<textarea name="description" id="anime-description" class="form-control" rows="5">{{ old('description', $anime->description) }}</textarea>
|
||||
<div id="ai-anime-status" class="form-text" style="display:none"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Detaylar --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-sliders" style="color:var(--info)"></i>
|
||||
<h6>Detaylar</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Tip</label>
|
||||
<select name="type" class="form-select">
|
||||
@foreach(['series'=>'Dizi','movie'=>'Film','ova'=>'OVA','ona'=>'ONA','special'=>'Special'] as $v => $l)
|
||||
<option value="{{ $v }}" {{ old('type',$anime->type)===$v?'selected':'' }}>{{ $l }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Durum</label>
|
||||
<select name="status" class="form-select">
|
||||
<option value="ongoing" {{ old('status',$anime->status)==='ongoing' ?'selected':'' }}>Devam Ediyor</option>
|
||||
<option value="completed" {{ old('status',$anime->status)==='completed' ?'selected':'' }}>Tamamlandı</option>
|
||||
<option value="upcoming" {{ old('status',$anime->status)==='upcoming' ?'selected':'' }}>Yakında</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Yayın Yılı</label>
|
||||
<input type="number" name="release_year" class="form-control" value="{{ old('release_year',$anime->release_year) }}" min="1900" max="2099">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Stüdyo</label>
|
||||
<input type="text" name="studio" class="form-control" value="{{ old('studio',$anime->studio) }}" placeholder="MAPPA, WIT Studio...">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Puan (0–10)</label>
|
||||
<input type="number" name="rating" class="form-control" step="0.1" min="0" max="10" value="{{ old('rating',$anime->rating) }}">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">
|
||||
MAL ID
|
||||
@foreach($anime->seasons as $s)
|
||||
@if($s->mal_id)<span class="badge bg-success ms-1" style="font-size:10px">S{{ $s->season_number }}:{{ $s->mal_id }}</span>@endif
|
||||
@endforeach
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<input type="text" name="mal_id" id="mal_id_input" class="form-control"
|
||||
value="{{ old('mal_id',$anime->mal_id) }}" placeholder="örn: 16498">
|
||||
<button type="button" class="btn btn-secondary btn-sm" id="btn-fetch-mal"
|
||||
onclick="fetchMalSeasons()" title="MAL ID kaydet + sezon ID'lerini Jikan'dan çek">
|
||||
<i class="bi bi-magic"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="mal-fetch-result" class="form-text">
|
||||
@if(!$anime->mal_id)
|
||||
<span style="color:var(--warning)">⚠ MAL ID girilmemiş — İntro Atla çalışmaz</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Fragman URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-youtube" style="color:#ff0000"></i></span>
|
||||
<input type="url" name="trailer_url" class="form-control" value="{{ old('trailer_url',$anime->trailer_url) }}" placeholder="https://youtube.com/...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Türler --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-tags" style="color:var(--warning)"></i>
|
||||
<h6>Türler</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
@foreach($genres as $genre)
|
||||
<label class="genre-chip">
|
||||
<input type="checkbox" name="genres[]" value="{{ $genre->id }}"
|
||||
{{ $anime->genres->contains($genre->id) ? 'checked' : '' }}>
|
||||
{{ $genre->name }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- İzinler --}}
|
||||
@if(isset($permissions) && count($permissions))
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-shield-lock" style="color:var(--success)"></i>
|
||||
<h6>İzin Ayarları</h6>
|
||||
<span style="font-size:12px;color:var(--text2)">— Global ayarları override eder</span>
|
||||
</div>
|
||||
<div class="section-body p-0">
|
||||
@foreach($permissions as $perm)
|
||||
@php $current = $contentPerms[$perm->key] ?? 'free'; @endphp
|
||||
<div class="perm-row">
|
||||
<div>
|
||||
<div class="perm-name">{{ $perm->label }}</div>
|
||||
@if($perm->description)<div class="perm-desc">{{ $perm->description }}</div>@endif
|
||||
</div>
|
||||
<div class="perm-toggle">
|
||||
<input type="radio" name="permissions[{{ $perm->key }}]" id="ep_{{ $perm->key }}_free" value="free" {{ $current==='free'?'checked':'' }}>
|
||||
<label for="ep_{{ $perm->key }}_free" class="free">Ücretsiz</label>
|
||||
<input type="radio" name="permissions[{{ $perm->key }}]" id="ep_{{ $perm->key }}_prem" value="premium" {{ $current==='premium'?'checked':'' }}>
|
||||
<label for="ep_{{ $perm->key }}_prem" class="premium">Premium</label>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
{{-- ── Sağ Kolon ── --}}
|
||||
<div class="col-xl-4 col-lg-5">
|
||||
|
||||
{{-- Kapak --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-image" style="color:var(--info)"></i>
|
||||
<h6>Kapak Görseli</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
@if($anime->coverUrl)
|
||||
<img src="{{ $anime->coverUrl }}" alt=""
|
||||
style="width:100%;aspect-ratio:2/3;object-fit:cover;border-radius:var(--radius-sm);margin-bottom:12px">
|
||||
@endif
|
||||
<label style="font-size:12px;color:var(--text2);margin-bottom:6px;display:block">
|
||||
{{ $anime->coverUrl ? 'Yeni görsel yükle (mevcut üzerine yazar)' : 'Kapak görseli yükle' }}
|
||||
</label>
|
||||
<input type="file" name="cover_image" class="form-control" accept="image/*">
|
||||
<div class="form-text">Önerilen: 300×450px</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Banner --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-panorama" style="color:var(--info)"></i>
|
||||
<h6>Banner Görseli</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<input type="file" name="banner_image" class="form-control" accept="image/*">
|
||||
<div class="form-text">Önerilen: 1280×720px</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Yayın --}}
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<i class="bi bi-toggle-on" style="color:var(--success)"></i>
|
||||
<h6>Yayın Ayarları</h6>
|
||||
</div>
|
||||
<div class="section-body d-flex flex-column gap-3">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<div style="font-size:13.5px;font-weight:600;color:var(--text)">Yayınla</div>
|
||||
<div style="font-size:12px;color:var(--text2)">Kullanıcılara görünür</div>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" name="is_published" value="1"
|
||||
id="is_published" role="switch" {{ $anime->is_published ? 'checked' : '' }}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<div style="font-size:13.5px;font-weight:600;color:var(--text)">Öne Çıkar</div>
|
||||
<div style="font-size:12px;color:var(--text2)">Ana sayfada öne çıkar</div>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" name="is_featured" value="1"
|
||||
id="is_featured" role="switch" {{ $anime->is_featured ? 'checked' : '' }}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<div style="font-size:13.5px;font-weight:600;color:var(--text)">Türkçe Dublaj</div>
|
||||
<div style="font-size:12px;color:var(--text2)">Ana sayfada Dublaj bölümünde göster</div>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" name="is_dubbed" value="1"
|
||||
id="is_dubbed" role="switch" {{ $anime->is_dubbed ? 'checked' : '' }}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100 btn-lg">
|
||||
<i class="bi bi-floppy"></i> Değişiklikleri Kaydet
|
||||
</button>
|
||||
<a href="{{ route('admin.animes.show', $anime) }}" class="btn btn-secondary w-100 mt-2">İptal</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
const AI_META_URL = '{{ route("admin.ai.anime-meta") }}';
|
||||
const AI_GEN_URL = '{{ route("admin.ai.generate") }}';
|
||||
const CSRF_TOKEN = document.querySelector('meta[name=csrf-token]')?.content || '';
|
||||
|
||||
async function aiAutoFill() {
|
||||
const btn = document.getElementById('ai-meta-btn');
|
||||
const status = document.getElementById('ai-meta-status');
|
||||
const title = document.querySelector('[name="title"]')?.value?.trim();
|
||||
if (!title) { axToast('Önce anime başlığını girin.', 'warning'); return; }
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1" style="width:12px;height:12px"></span> Sorgulanıyor...';
|
||||
status.style.display = 'block';
|
||||
status.style.background = 'var(--info-dim)';
|
||||
status.style.color = 'var(--info)';
|
||||
status.textContent = 'DeepSeek sorgulanıyor, lütfen bekleyin (~5-10 sn)...';
|
||||
|
||||
try {
|
||||
const res = await fetch(AI_META_URL, { method:'POST', headers:{'Content-Type':'application/json','X-CSRF-TOKEN':CSRF_TOKEN}, body:JSON.stringify({ title, title_jp: document.querySelector('[name="title_jp"]')?.value?.trim()||'' }) });
|
||||
const data = await res.json();
|
||||
if (!res.ok || data.error) { status.style.background='var(--danger-dim)'; status.style.color='var(--danger)'; status.textContent='❌ '+(data.error||'Hata'); return; }
|
||||
|
||||
const m = data.meta; let filled = [];
|
||||
const fillF = (sel, val) => { if (!val) return; const el = document.querySelector(sel); if (el && !el.value?.trim()) { el.value = val; filled.push(el.name||sel); } };
|
||||
fillF('[name="description"]',m.description); fillF('[name="title_en"]',m.title_en); fillF('[name="title_jp"]',m.title_jp);
|
||||
fillF('[name="studio"]',m.studio); fillF('[name="release_year"]',m.release_year); fillF('[name="rating"]',m.rating); fillF('[name="trailer_url"]',m.trailer_url);
|
||||
const fillS = (sel, val) => { if (!val) return; const el = document.querySelector(sel); if (!el) return; const opt = [...el.options].find(o => o.value===val); if (opt) { el.value=val; filled.push(el.name); } };
|
||||
fillS('[name="type"]',m.type); fillS('[name="status"]',m.status);
|
||||
if (Array.isArray(m.genres) && m.genres.length) {
|
||||
document.querySelectorAll('[name="genres[]"]').forEach(cb => {
|
||||
const lbl = cb.closest('label')?.textContent?.trim()?.toLowerCase();
|
||||
if (!lbl) return;
|
||||
if (m.genres.some(g => lbl.includes(g.toLowerCase()) || g.toLowerCase().includes(lbl)) && !cb.checked) { cb.checked=true; filled.push('genre'); }
|
||||
});
|
||||
}
|
||||
status.style.background='var(--success-dim)'; status.style.color='var(--success)';
|
||||
status.textContent = `✓ ${filled.length} alan dolduruldu. Kontrol edip kaydedin.`;
|
||||
} catch(e) { status.style.background='var(--danger-dim)'; status.style.color='var(--danger)'; status.textContent='❌ Bağlantı hatası: '+e.message; }
|
||||
finally { btn.disabled=false; btn.innerHTML='<i class="bi bi-magic"></i> Tümünü Doldur'; }
|
||||
}
|
||||
|
||||
async function aiGenerateAnime() {
|
||||
const btn = document.getElementById('ai-anime-btn');
|
||||
const status = document.getElementById('ai-anime-status');
|
||||
const ta = document.getElementById('anime-description');
|
||||
const title = document.querySelector('[name="title"]')?.value?.trim();
|
||||
if (!title) { axToast('Önce başlık girin.', 'warning'); return; }
|
||||
|
||||
btn.disabled=true; btn.innerHTML='<span class="spinner-border spinner-border-sm" style="width:12px;height:12px"></span>';
|
||||
status.style.display='block'; status.className='form-text'; status.style.color='var(--info)'; status.textContent='AI yazıyor...';
|
||||
try {
|
||||
const res = await fetch(AI_GEN_URL, { method:'POST', headers:{'Content-Type':'application/json','X-CSRF-TOKEN':CSRF_TOKEN}, body:JSON.stringify({ type:'anime', title, title_jp: document.querySelector('[name="title_jp"]')?.value?.trim(), genres:[...document.querySelectorAll('[name="genres[]"]:checked')].map(el=>el.closest('label')?.textContent?.trim()||el.value).join(', ') }) });
|
||||
const data = await res.json();
|
||||
if (data.error) { status.style.color='var(--danger)'; status.textContent='❌ '+data.error; }
|
||||
else { ta.value=data.description; status.style.color='var(--success)'; status.textContent='✓ Açıklama oluşturuldu.'; }
|
||||
} catch(e) { status.style.color='var(--danger)'; status.textContent='❌ Hata.'; }
|
||||
finally { btn.disabled=false; btn.innerHTML='<i class="bi bi-stars" style="color:var(--info)"></i> AI ile Yaz'; }
|
||||
}
|
||||
|
||||
async function fetchMalSeasons() {
|
||||
const btn=document.getElementById('btn-fetch-mal'); const result=document.getElementById('mal-fetch-result');
|
||||
const malId=document.getElementById('mal_id_input').value.trim();
|
||||
if (!malId) { result.innerHTML='<span style="color:var(--warning)">⚠ MAL ID girin.</span>'; return; }
|
||||
btn.disabled=true; btn.innerHTML='<span class="spinner-border spinner-border-sm" style="width:12px;height:12px"></span>';
|
||||
result.innerHTML='<span style="color:var(--text2)">Jikan API\'ye bağlanılıyor...</span>';
|
||||
try {
|
||||
await fetch('{{ route("admin.animes.update", $anime) }}', { method:'POST', headers:{'X-CSRF-TOKEN':CSRF_TOKEN,'Accept':'application/json','Content-Type':'application/x-www-form-urlencoded'}, body:new URLSearchParams({_method:'PUT',mal_id:malId}) });
|
||||
const res = await fetch('{{ route("admin.animes.fetch-mal-seasons", $anime) }}', { method:'POST', headers:{'X-CSRF-TOKEN':CSRF_TOKEN,'Accept':'application/json'} });
|
||||
const data = await res.json();
|
||||
if (data.success) { const rows=data.updated.map(u=>`S${u.season}→${u.mal_id}`).join(', '); result.innerHTML=`<span style="color:var(--success)">✓ ${data.message}${rows?' ('+rows+')':''}</span>`; }
|
||||
else { result.innerHTML=`<span style="color:var(--danger)">❌ ${data.error}</span>`; }
|
||||
} catch(e) { result.innerHTML='<span style="color:var(--danger)">❌ Hata.</span>'; }
|
||||
finally { btn.disabled=false; btn.innerHTML='<i class="bi bi-magic"></i>'; }
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,488 @@
|
||||
@extends('admin.layouts.app')
|
||||
@section('title', 'Animeler')
|
||||
@section('page-title', 'Animeler')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.anime-cover {
|
||||
width: 40px; height: 58px;
|
||||
object-fit: cover;
|
||||
border-radius: 6px;
|
||||
background: var(--surface);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.anime-cover-placeholder {
|
||||
width: 40px; height: 58px;
|
||||
border-radius: 6px;
|
||||
background: var(--surface);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: var(--text3);
|
||||
flex-shrink: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
.anime-title { font-size: 13.5px; font-weight: 600; color: var(--text); }
|
||||
.anime-genres { font-size: 12px; color: var(--text2); margin-top: 2px; }
|
||||
|
||||
.bulk-bar {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 16px;
|
||||
background: var(--accent-dim);
|
||||
border: 1px solid rgba(124,58,237,0.3);
|
||||
border-radius: var(--radius-sm);
|
||||
margin-bottom: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.bulk-bar.on { display: flex; }
|
||||
.bulk-bar-count { font-size: 13px; font-weight: 700; color: var(--accent-lt); }
|
||||
|
||||
.mal-progress-card {
|
||||
display: none;
|
||||
background: var(--bg2);
|
||||
border: 1px solid var(--border2);
|
||||
border-radius: var(--radius);
|
||||
padding: 18px 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.mal-progress-card.on { display: block; }
|
||||
.mal-log {
|
||||
font-size: 12px;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
color: var(--text2);
|
||||
max-height: 130px;
|
||||
overflow-y: auto;
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
margin-top: 10px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* Action dropdown */
|
||||
.act-drop { position: relative; }
|
||||
.act-drop-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 0; top: calc(100% + 4px);
|
||||
min-width: 160px;
|
||||
background: var(--bg2);
|
||||
border: 1px solid var(--border2);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 5px;
|
||||
z-index: 200;
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
.act-drop-menu.on { display: block; }
|
||||
.act-drop-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 7px 11px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
color: var(--text2);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
background: none;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
transition: all 0.12s;
|
||||
font-family: inherit;
|
||||
}
|
||||
.act-drop-item:hover { background: rgba(255,255,255,0.06); color: var(--text); }
|
||||
.act-drop-item.danger:hover { background: var(--danger-dim); color: var(--danger); }
|
||||
.act-drop-sep { height: 1px; background: var(--border); margin: 4px 0; }
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
|
||||
{{-- ── Page Header ── --}}
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h5 style="font-size:18px;font-weight:700;color:var(--text);margin:0">
|
||||
Animeler
|
||||
<span style="font-size:13px;font-weight:600;color:var(--text2);margin-left:6px">{{ number_format($animes->total()) }} toplam</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="page-header-actions">
|
||||
@if($zeroEpisodeCount > 0)
|
||||
<button class="btn btn-outline-warning btn-sm" onclick="destroyZeroEpisodes()" id="btn-zero-ep">
|
||||
<i class="bi bi-trash3"></i>
|
||||
<span class="d-none d-md-inline">0 Bölümlü Sil</span>
|
||||
<span class="badge bg-warning" style="color:#000">{{ $zeroEpisodeCount }}</span>
|
||||
</button>
|
||||
@endif
|
||||
<button class="btn btn-secondary btn-sm" onclick="bulkFindMal()" id="btn-bulk-mal">
|
||||
<i class="bi bi-magic"></i>
|
||||
<span class="d-none d-md-inline">MAL ID Otomatik Bul</span>
|
||||
</button>
|
||||
<a href="{{ route('admin.animes.create') }}" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-plus-lg"></i> Anime Ekle
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── MAL Progress Card ── --}}
|
||||
<div class="mal-progress-card" id="bulk-mal-box">
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<div style="font-size:14px;font-weight:700;color:var(--text);display:flex;align-items:center;gap:8px">
|
||||
<i class="bi bi-magic" style="color:var(--info)"></i> MAL ID Otomatik Doldurma
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="stopBulkMal()"><i class="bi bi-stop-fill"></i> Durdur</button>
|
||||
</div>
|
||||
<div style="background:var(--bg);border-radius:99px;height:6px;overflow:hidden">
|
||||
<div id="bulk-mal-bar" style="height:100%;width:0%;background:linear-gradient(90deg,var(--accent),var(--accent-lt));border-radius:99px;transition:width .3s"></div>
|
||||
</div>
|
||||
<div class="mal-log" id="bulk-mal-log"></div>
|
||||
</div>
|
||||
|
||||
{{-- ── Filters ── --}}
|
||||
<div class="filter-card mb-3">
|
||||
<form method="GET" class="d-flex flex-wrap gap-2 align-items-end">
|
||||
<div class="search-input-wrap flex-grow-1" style="min-width:180px;max-width:320px">
|
||||
<i class="bi bi-search"></i>
|
||||
<input type="text" name="search" class="form-control" placeholder="Anime ara..." value="{{ request('search') }}">
|
||||
</div>
|
||||
<select name="status" class="form-select" style="width:auto;min-width:140px">
|
||||
<option value="">Tüm Durumlar</option>
|
||||
<option value="ongoing" {{ request('status')=='ongoing' ?'selected':'' }}>Devam Ediyor</option>
|
||||
<option value="completed" {{ request('status')=='completed' ?'selected':'' }}>Tamamlandı</option>
|
||||
<option value="upcoming" {{ request('status')=='upcoming' ?'selected':'' }}>Yakında</option>
|
||||
</select>
|
||||
<select name="type" class="form-select" style="width:auto;min-width:120px">
|
||||
<option value="">Tüm Tipler</option>
|
||||
<option value="series" {{ request('type')=='series'?'selected':'' }}>Dizi</option>
|
||||
<option value="movie" {{ request('type')=='movie' ?'selected':'' }}>Film</option>
|
||||
<option value="ova" {{ request('type')=='ova' ?'selected':'' }}>OVA</option>
|
||||
</select>
|
||||
<select name="no_episodes" class="form-select" style="width:auto;min-width:150px">
|
||||
<option value="">Tüm Animeler</option>
|
||||
<option value="1" {{ request('no_episodes')=='1'?'selected':'' }}>Sadece 0 Bölümlüler</option>
|
||||
</select>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm"><i class="bi bi-funnel"></i> Filtrele</button>
|
||||
<a href="{{ route('admin.animes.index') }}" class="btn btn-secondary btn-sm"><i class="bi bi-x-lg"></i></a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- ── Bulk Selection Bar ── --}}
|
||||
<div class="bulk-bar" id="bulk-bar">
|
||||
<span class="bulk-bar-count" id="bulk-count">0 anime seçildi</span>
|
||||
<span class="badge bg-info d-none" id="bulk-all-note" style="color:#000">Tüm sayfalardaki animeler</span>
|
||||
<div style="margin-left:auto;display:flex;gap:8px;flex-wrap:wrap">
|
||||
<button class="btn btn-sm btn-secondary d-none" id="btn-sel-page" onclick="selPage()">
|
||||
<i class="bi bi-check2-square"></i> Sayfayı Seç
|
||||
</button>
|
||||
<button class="btn btn-sm btn-secondary d-none" id="btn-sel-all" onclick="selAllPages()">
|
||||
<i class="bi bi-check2-all"></i> Tümünü Seç ({{ $animes->total() }})
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="bulkDelete()">
|
||||
<i class="bi bi-trash3"></i> Seçilenleri Sil
|
||||
</button>
|
||||
<button class="btn btn-sm btn-secondary" onclick="clearSel()">
|
||||
<i class="bi bi-x-lg"></i> İptal
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── Table ── --}}
|
||||
<div class="table-wrap">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:44px;padding-left:20px">
|
||||
<input type="checkbox" class="form-check-input" id="chk-page" onchange="togglePage(this.checked)">
|
||||
</th>
|
||||
<th>Anime</th>
|
||||
<th>Tip / Durum</th>
|
||||
<th>MAL ID</th>
|
||||
<th style="text-align:center">Bölüm</th>
|
||||
<th style="text-align:center">Yayın</th>
|
||||
<th style="text-align:right;padding-right:20px">İşlem</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($animes as $anime)
|
||||
<tr id="anime-row-{{ $anime->id }}">
|
||||
<td style="padding-left:20px">
|
||||
<input type="checkbox" class="form-check-input chk-item" value="{{ $anime->id }}" onchange="onCheck()">
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
@if($anime->coverUrl)
|
||||
<img src="{{ $anime->coverUrl }}" class="anime-cover" alt="">
|
||||
@else
|
||||
<div class="anime-cover-placeholder"><i class="bi bi-image"></i></div>
|
||||
@endif
|
||||
<div class="overflow-hidden">
|
||||
<div class="anime-title text-truncate" style="max-width:260px">{{ $anime->title }}</div>
|
||||
<div class="anime-genres">{{ $anime->genres->pluck('name')->join(', ') ?: '—' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column gap-1">
|
||||
<span class="badge bg-secondary" style="width:fit-content">{{ strtoupper($anime->type) }}</span>
|
||||
@if($anime->status === 'ongoing')
|
||||
<span class="badge bg-success" style="width:fit-content">Devam Ediyor</span>
|
||||
@elseif($anime->status === 'completed')
|
||||
<span class="badge bg-info" style="width:fit-content;color:#000">Tamamlandı</span>
|
||||
@else
|
||||
<span class="badge bg-warning" style="width:fit-content;color:#000">Yakında</span>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
<td class="mal-id-cell" data-id="{{ $anime->id }}" data-fetch-url="{{ route('admin.animes.fetch-mal', $anime) }}">
|
||||
@if($anime->mal_id)
|
||||
<a href="https://myanimelist.net/anime/{{ $anime->mal_id }}" target="_blank"
|
||||
class="badge bg-success text-decoration-none mal-badge-{{ $anime->id }}" style="color:#fff">
|
||||
<i class="bi bi-box-arrow-up-right" style="font-size:10px;margin-right:3px"></i>{{ $anime->mal_id }}
|
||||
</a>
|
||||
@else
|
||||
<span class="mal-badge-{{ $anime->id }}" style="display:inline-flex;align-items:center;gap:5px">
|
||||
<span class="badge bg-danger">Eksik</span>
|
||||
<button class="btn btn-link btn-sm p-0" style="font-size:12px;color:var(--info)" title="Otomatik Bul"
|
||||
onclick="fetchSingleMal({{ $anime->id }}, this)">
|
||||
<i class="bi bi-magic"></i>
|
||||
</button>
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
<td style="text-align:center">
|
||||
<span style="font-size:14px;font-weight:700;color:{{ $anime->episode_count > 0 ? 'var(--text)' : 'var(--danger)' }}">
|
||||
{{ $anime->episode_count }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="text-align:center">
|
||||
@if($anime->is_published)
|
||||
<span style="display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:600;color:var(--success)">
|
||||
<i class="bi bi-circle-fill" style="font-size:7px"></i> Yayında
|
||||
</span>
|
||||
@else
|
||||
<span style="display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:600;color:var(--text3)">
|
||||
<i class="bi bi-circle" style="font-size:7px"></i> Taslak
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
<td style="text-align:right;padding-right:20px">
|
||||
<div class="act-drop">
|
||||
<button class="btn btn-secondary btn-sm" onclick="toggleDrop(this)">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="act-drop-menu">
|
||||
<a href="{{ route('admin.animes.show', $anime) }}" class="act-drop-item">
|
||||
<i class="bi bi-eye"></i> Görüntüle
|
||||
</a>
|
||||
<a href="{{ route('admin.animes.edit', $anime) }}" class="act-drop-item">
|
||||
<i class="bi bi-pencil"></i> Düzenle
|
||||
</a>
|
||||
<a href="{{ route('admin.episodes.index', ['anime_id' => $anime->id]) }}" class="act-drop-item">
|
||||
<i class="bi bi-collection-play"></i> Bölümler
|
||||
</a>
|
||||
<div class="act-drop-sep"></div>
|
||||
<form method="POST" action="{{ route('admin.animes.destroy', $anime) }}"
|
||||
onsubmit="return confirm('{{ addslashes($anime->title) }} silinsin mi?')">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="act-drop-item danger">
|
||||
<i class="bi bi-trash3"></i> Sil
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<div class="empty-state">
|
||||
<i class="bi bi-play-circle"></i>
|
||||
<h6>Anime bulunamadı</h6>
|
||||
<p>Arama kriterlerinizi değiştirin veya yeni anime ekleyin.</p>
|
||||
<a href="{{ route('admin.animes.create') }}" class="btn btn-primary btn-sm mt-2">
|
||||
<i class="bi bi-plus-lg"></i> Anime Ekle
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── Pagination ── --}}
|
||||
<div class="mt-3">
|
||||
{{ $animes->links('admin.partials.pagination') }}
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
const BULK_MAL_URL = '{{ route("admin.animes.bulk-find-mal") }}';
|
||||
const BULK_DESTROY_URL = '{{ route("admin.animes.bulk-destroy") }}';
|
||||
const ZERO_DESTROY_URL = '{{ route("admin.animes.destroy-zero-episodes") }}';
|
||||
const TOTAL_ANIMES = {{ $animes->total() }};
|
||||
const CSRF = document.querySelector('meta[name=csrf-token]')?.content || '';
|
||||
|
||||
/* ── Action dropdown ── */
|
||||
function toggleDrop(btn) {
|
||||
const menu = btn.nextElementSibling;
|
||||
const wasOn = menu.classList.contains('on');
|
||||
document.querySelectorAll('.act-drop-menu.on').forEach(m => m.classList.remove('on'));
|
||||
if (!wasOn) menu.classList.add('on');
|
||||
}
|
||||
document.addEventListener('click', e => {
|
||||
if (!e.target.closest('.act-drop')) {
|
||||
document.querySelectorAll('.act-drop-menu.on').forEach(m => m.classList.remove('on'));
|
||||
}
|
||||
});
|
||||
|
||||
/* ── Zero episode delete ── */
|
||||
async function destroyZeroEpisodes() {
|
||||
if (!confirm('0 bölümlü tüm animeler silinsin mi? Bu işlem geri alınamaz.')) return;
|
||||
const btn = document.getElementById('btn-zero-ep');
|
||||
if (btn) { btn.disabled = true; btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span>'; }
|
||||
try {
|
||||
const res = await fetch(ZERO_DESTROY_URL, { method:'POST', headers:{'X-CSRF-TOKEN':CSRF,'Accept':'application/json'} });
|
||||
const d = await res.json();
|
||||
if (d.success) { axToast(`${d.count} anime silindi.`, 'success'); setTimeout(() => location.reload(), 1200); }
|
||||
else { axToast('Hata oluştu.', 'error'); if (btn) btn.disabled = false; }
|
||||
} catch(e) { axToast('Hata: ' + e.message, 'error'); if (btn) btn.disabled = false; }
|
||||
}
|
||||
|
||||
/* ── Bulk selection ── */
|
||||
let _selAll = false;
|
||||
|
||||
function _selBtns(show) {
|
||||
document.getElementById('btn-sel-page').classList.toggle('d-none', !show);
|
||||
document.getElementById('btn-sel-all').classList.toggle('d-none', !show);
|
||||
}
|
||||
|
||||
function selPage() {
|
||||
document.querySelectorAll('.chk-item').forEach(c => c.checked = true);
|
||||
document.getElementById('chk-page').checked = true;
|
||||
_selAll = false;
|
||||
document.getElementById('bulk-all-note').classList.add('d-none');
|
||||
onCheck();
|
||||
}
|
||||
function selAllPages() {
|
||||
selPage(); _selAll = true;
|
||||
document.getElementById('bulk-all-note').classList.remove('d-none');
|
||||
updateBar();
|
||||
}
|
||||
function clearSel() {
|
||||
document.querySelectorAll('.chk-item').forEach(c => c.checked = false);
|
||||
document.getElementById('chk-page').checked = false;
|
||||
_selAll = false;
|
||||
document.getElementById('bulk-all-note').classList.add('d-none');
|
||||
updateBar();
|
||||
}
|
||||
function togglePage(checked) {
|
||||
document.querySelectorAll('.chk-item').forEach(c => c.checked = checked);
|
||||
_selAll = false;
|
||||
document.getElementById('bulk-all-note').classList.add('d-none');
|
||||
onCheck();
|
||||
}
|
||||
function onCheck() {
|
||||
const all = document.querySelectorAll('.chk-item');
|
||||
const chk = document.querySelectorAll('.chk-item:checked');
|
||||
document.getElementById('chk-page').checked = all.length > 0 && all.length === chk.length;
|
||||
_selAll = false;
|
||||
document.getElementById('bulk-all-note').classList.add('d-none');
|
||||
updateBar();
|
||||
}
|
||||
function updateBar() {
|
||||
const chk = document.querySelectorAll('.chk-item:checked');
|
||||
const bar = document.getElementById('bulk-bar');
|
||||
const cnt = document.getElementById('bulk-count');
|
||||
if (_selAll) {
|
||||
cnt.textContent = `Tüm ${TOTAL_ANIMES} anime seçildi`;
|
||||
bar.classList.add('on'); _selBtns(false);
|
||||
} else if (chk.length > 0) {
|
||||
cnt.textContent = `${chk.length} anime seçildi`;
|
||||
bar.classList.add('on'); _selBtns(true);
|
||||
} else {
|
||||
bar.classList.remove('on'); _selBtns(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function bulkDelete() {
|
||||
const chk = [...document.querySelectorAll('.chk-item:checked')].map(c => c.value);
|
||||
const label = _selAll ? `tüm ${TOTAL_ANIMES} animeyi` : `${chk.length} animeyi`;
|
||||
if (!confirm(`${label} silmek istediğinize emin misiniz?\nBu işlem CDN dosyalarını da siler!`)) return;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const body = _selAll
|
||||
? { all: true, filters: { search: params.get('search')||'', status: params.get('status')||'', type: params.get('type')||'' } }
|
||||
: { ids: chk };
|
||||
try {
|
||||
const res = await fetch(BULK_DESTROY_URL, { method:'POST', headers:{'X-CSRF-TOKEN':CSRF,'Content-Type':'application/json','Accept':'application/json'}, body:JSON.stringify(body) });
|
||||
const d = await res.json();
|
||||
if (d.success) {
|
||||
if (_selAll) { location.reload(); return; }
|
||||
chk.forEach(id => document.getElementById(`anime-row-${id}`)?.remove());
|
||||
clearSel();
|
||||
axToast(`${chk.length} anime silindi.`, 'success');
|
||||
} else { axToast('Hata: ' + (d.message || '?'), 'error'); }
|
||||
} catch(e) { axToast('Hata: ' + e.message, 'error'); }
|
||||
}
|
||||
|
||||
/* ── Bulk MAL find ── */
|
||||
let bulkRunning = false, skipIds = [], totalDone = 0, totalInitial = 0;
|
||||
|
||||
async function bulkFindMal() {
|
||||
if (bulkRunning) return;
|
||||
bulkRunning = true; skipIds = []; totalDone = 0; totalInitial = 0;
|
||||
document.getElementById('bulk-mal-box').classList.add('on');
|
||||
document.getElementById('btn-bulk-mal').disabled = true;
|
||||
document.getElementById('bulk-mal-log').innerHTML = '';
|
||||
document.getElementById('bulk-mal-bar').style.width = '0%';
|
||||
log('⏳ Başlatılıyor...');
|
||||
await runNext();
|
||||
}
|
||||
|
||||
async function runNext() {
|
||||
if (!bulkRunning) return;
|
||||
try {
|
||||
const res = await fetch(BULK_MAL_URL, { method:'POST', headers:{'X-CSRF-TOKEN':CSRF,'Content-Type':'application/json','Accept':'application/json'}, body:JSON.stringify({skip_ids:skipIds}) });
|
||||
const d = await res.json();
|
||||
if (d.done) { log('✅ ' + d.message); finish(); return; }
|
||||
totalDone++;
|
||||
if (!totalInitial) totalInitial = totalDone + (d.remaining ?? 0);
|
||||
const pct = totalInitial > 0 ? Math.round((totalDone / totalInitial) * 100) : 0;
|
||||
document.getElementById('bulk-mal-bar').style.width = pct + '%';
|
||||
if (d.found) log(`✓ <b>${esc(d.anime)}</b> → ${d.mal_id}`);
|
||||
else { log(`<span style="opacity:.5">— ${esc(d.anime)}: bulunamadı</span>`); if (d.skipped_id) skipIds.push(d.skipped_id); }
|
||||
setTimeout(runNext, 450);
|
||||
} catch(e) { log(`<span style="color:var(--danger)">❌ ${e.message}</span>`); setTimeout(runNext, 2000); }
|
||||
}
|
||||
|
||||
function stopBulkMal() { bulkRunning = false; log('⏹ Durduruldu.'); finish(); }
|
||||
function finish() { bulkRunning = false; document.getElementById('btn-bulk-mal').disabled = false; document.getElementById('bulk-mal-bar').style.width = '100%'; }
|
||||
function log(html) { const el = document.getElementById('bulk-mal-log'); el.innerHTML += html + '<br>'; el.scrollTop = el.scrollHeight; }
|
||||
function esc(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
||||
|
||||
async function fetchSingleMal(animeId, btn) {
|
||||
const cell = btn.closest('.mal-id-cell');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm" style="width:12px;height:12px;border-width:1.5px"></span>';
|
||||
try {
|
||||
const res = await fetch(cell.dataset.fetchUrl, { method:'POST', headers:{'X-CSRF-TOKEN':CSRF,'Accept':'application/json'} });
|
||||
const d = await res.json();
|
||||
if (d.found) {
|
||||
cell.innerHTML = `<a href="https://myanimelist.net/anime/${d.mal_id}" target="_blank" class="badge bg-success text-decoration-none" style="color:#fff"><i class="bi bi-box-arrow-up-right" style="font-size:10px;margin-right:3px"></i>${d.mal_id}</a>`;
|
||||
} else {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="bi bi-magic"></i>';
|
||||
btn.title = 'Bulunamadı — tekrar dene';
|
||||
}
|
||||
} catch(e) { btn.disabled = false; btn.innerHTML = '<i class="bi bi-exclamation-triangle" style="color:var(--danger)"></i>'; }
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,247 @@
|
||||
@extends('admin.layouts.app')
|
||||
@section('title', $anime->title)
|
||||
@section('page-title', $anime->title)
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="d-flex align-items-center gap-3 mb-4 flex-wrap">
|
||||
<a href="{{ route('admin.animes.index') }}" class="btn btn-secondary btn-sm">
|
||||
<i class="bi bi-arrow-left"></i> Animelere Dön
|
||||
</a>
|
||||
<div style="margin-left:auto;display:flex;gap:8px;flex-wrap:wrap">
|
||||
<button class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addSeasonModal">
|
||||
<i class="bi bi-plus-circle"></i> Sezon Ekle
|
||||
</button>
|
||||
<a href="{{ route('admin.animes.edit', $anime) }}" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-pencil"></i> Düzenle
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
|
||||
{{-- ── Sol: Sezonlar & Bölümler ── --}}
|
||||
<div class="col-lg-8">
|
||||
@forelse($anime->seasons as $season)
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<span style="font-size:14px;font-weight:700;color:var(--text)">{{ $season->season_number }}. Sezon</span>
|
||||
@if($season->title)
|
||||
<span style="font-size:13px;color:var(--text2);margin-left:6px">— {{ $season->title }}</span>
|
||||
@endif
|
||||
@if($season->mal_id)
|
||||
<span class="badge bg-success ms-2" style="font-size:10px">MAL: {{ $season->mal_id }}</span>
|
||||
@endif
|
||||
</div>
|
||||
<div style="margin-left:auto;display:flex;gap:7px">
|
||||
<a href="{{ route('admin.episodes.create', ['anime_id'=>$anime->id,'season_id'=>$season->id]) }}"
|
||||
class="btn btn-sm btn-success">
|
||||
<i class="bi bi-plus-lg"></i> Bölüm Ekle
|
||||
</a>
|
||||
<form method="POST" action="{{ route('admin.seasons.destroy', $season) }}"
|
||||
onsubmit="return confirm('{{ $season->season_number }}. sezon ve tüm bölümleri silinecek. Devam edilsin mi?')">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-trash3"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@if($season->episodes->count() > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding-left:20px">Bölüm</th>
|
||||
<th>Başlık</th>
|
||||
<th style="text-align:center">Durum</th>
|
||||
<th style="text-align:center">İzlenme</th>
|
||||
<th style="text-align:right;padding-right:20px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($season->episodes as $ep)
|
||||
<tr>
|
||||
<td style="padding-left:20px">
|
||||
<span style="font-family:monospace;font-size:12.5px;background:var(--accent-dim);color:var(--accent-lt);padding:2px 8px;border-radius:5px">
|
||||
{{ str_pad($ep->episode_number,2,'0',STR_PAD_LEFT) }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="font-size:13px;color:var(--text2)">{{ $ep->title ?: '—' }}</td>
|
||||
<td style="text-align:center">
|
||||
@if($ep->is_published)
|
||||
<span class="badge bg-success">Yayında</span>
|
||||
@else
|
||||
<span class="badge bg-secondary">Taslak</span>
|
||||
@endif
|
||||
</td>
|
||||
<td style="text-align:center;font-size:13px;color:var(--text2)">{{ number_format($ep->view_count) }}</td>
|
||||
<td style="text-align:right;padding-right:20px">
|
||||
<a href="{{ route('admin.episodes.edit', $ep) }}" class="btn btn-sm btn-secondary">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<div class="empty-state py-4">
|
||||
<i class="bi bi-collection-play"></i>
|
||||
<p>Henüz bölüm yok.</p>
|
||||
<a href="{{ route('admin.episodes.create', ['anime_id'=>$anime->id,'season_id'=>$season->id]) }}"
|
||||
class="btn btn-sm btn-success mt-1"><i class="bi bi-plus-lg"></i> Bölüm Ekle</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@empty
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="empty-state">
|
||||
<i class="bi bi-layers"></i>
|
||||
<h6>Henüz sezon eklenmemiş</h6>
|
||||
<button class="btn btn-primary btn-sm mt-2" data-bs-toggle="modal" data-bs-target="#addSeasonModal">
|
||||
<i class="bi bi-plus-circle"></i> İlk Sezonu Ekle
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforelse
|
||||
|
||||
{{-- İzin Ayarları --}}
|
||||
@if(isset($permissions) && count($permissions))
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-shield-lock" style="color:var(--success)"></i>
|
||||
<h6>Bu Anime İçin İzin Ayarları</h6>
|
||||
<span style="font-size:12px;color:var(--text2)">— Global ayarları override eder</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<form method="POST" action="{{ route('admin.animes.permissions', $anime) }}">
|
||||
@csrf
|
||||
@foreach($permissions as $perm)
|
||||
@php $current = $contentPerms[$perm->key] ?? 'free'; @endphp
|
||||
<div class="perm-row">
|
||||
<div>
|
||||
<div class="perm-name">{{ $perm->label }}</div>
|
||||
@if($perm->description)<div class="perm-desc">{{ $perm->description }}</div>@endif
|
||||
</div>
|
||||
<div class="perm-toggle">
|
||||
<input type="radio" name="permissions[{{ $perm->key }}]" id="ap_{{ $perm->key }}_free" value="free" {{ $current==='free'?'checked':'' }}>
|
||||
<label for="ap_{{ $perm->key }}_free" class="free">Ücretsiz</label>
|
||||
<input type="radio" name="permissions[{{ $perm->key }}]" id="ap_{{ $perm->key }}_prem" value="premium" {{ $current==='premium'?'checked':'' }}>
|
||||
<label for="ap_{{ $perm->key }}_prem" class="premium">Premium</label>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
<div class="p-3">
|
||||
<button type="submit" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-floppy"></i> İzinleri Kaydet
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- ── Sağ: Anime Bilgileri ── --}}
|
||||
<div class="col-lg-4">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body text-center" style="padding:24px">
|
||||
@if($anime->coverUrl)
|
||||
<img src="{{ $anime->coverUrl }}" alt="{{ $anime->title }}"
|
||||
style="width:100%;max-width:180px;aspect-ratio:2/3;object-fit:cover;border-radius:var(--radius-sm);margin-bottom:16px">
|
||||
@endif
|
||||
<div style="font-size:16px;font-weight:800;color:var(--text);margin-bottom:4px">{{ $anime->title }}</div>
|
||||
@if($anime->title_en)
|
||||
<div style="font-size:12.5px;color:var(--text2);margin-bottom:12px">{{ $anime->title_en }}</div>
|
||||
@endif
|
||||
<div class="d-flex justify-content-center gap-2 flex-wrap">
|
||||
<span class="badge bg-secondary">{{ strtoupper($anime->type) }}</span>
|
||||
<span class="badge {{ $anime->is_published?'bg-success':'bg-secondary' }}">
|
||||
{{ $anime->is_published?'Yayında':'Taslak' }}
|
||||
</span>
|
||||
@if($anime->is_featured)
|
||||
<span class="badge bg-warning" style="color:#000">Öne Çıkan</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-info-circle" style="color:var(--accent-lt)"></i>
|
||||
<h6>Detaylar</h6>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@foreach([
|
||||
['Durum', $anime->status, ''],
|
||||
['Yayın Yılı', $anime->release_year ?: '—', ''],
|
||||
['Stüdyo', $anime->studio ?: '—', ''],
|
||||
['Puan', $anime->rating ?: '—', ''],
|
||||
['MAL ID', $anime->mal_id ?: '—', ''],
|
||||
['Türler', $anime->genres->pluck('name')->join(', ') ?: '—', ''],
|
||||
] as [$lbl, $val])
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;padding:10px 16px;border-bottom:1px solid var(--border)">
|
||||
<span style="font-size:12.5px;color:var(--text2)">{{ $lbl }}</span>
|
||||
<span style="font-size:13px;font-weight:600;color:var(--text);text-align:right;max-width:60%">{{ $val }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<a href="{{ route('admin.animes.edit', $anime) }}" class="btn btn-primary">
|
||||
<i class="bi bi-pencil"></i> Düzenle
|
||||
</a>
|
||||
<a href="{{ route('admin.episodes.index', ['anime_id'=>$anime->id]) }}" class="btn btn-secondary">
|
||||
<i class="bi bi-collection-play"></i> Tüm Bölümleri Gör
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Sezon Ekle Modal --}}
|
||||
<div class="modal fade" id="addSeasonModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h6 class="modal-title"><i class="bi bi-plus-circle me-2" style="color:var(--accent-lt)"></i>Sezon Ekle</h6>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<form method="POST" action="{{ route('admin.animes.seasons.store', $anime) }}">
|
||||
@csrf
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Sezon Numarası <span style="color:var(--danger)">*</span></label>
|
||||
<input type="number" name="season_number" class="form-control"
|
||||
value="{{ $anime->seasons->count() + 1 }}" min="1" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Başlık <span style="color:var(--text2);font-size:11px">(opsiyonel)</span></label>
|
||||
<input type="text" name="title" class="form-control" placeholder="1. Sezon">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Yayın Yılı</label>
|
||||
<input type="number" name="release_year" class="form-control" value="{{ date('Y') }}">
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-between p-3 rounded" style="background:var(--surface2);border:1px solid var(--border)">
|
||||
<div style="font-size:13.5px;font-weight:600;color:var(--text)">Yayınla</div>
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" name="is_published" value="1" id="sp" role="switch" checked>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">İptal</button>
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-plus-circle"></i> Sezon Ekle</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
Reference in New Issue
Block a user