Initial commit: Animexe Laravel platform

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 00:01:48 +03:00
co-authored by Claude Opus 4.8
commit a63515cfc6
366 changed files with 74773 additions and 0 deletions
+462
View File
@@ -0,0 +1,462 @@
@extends('frontend.layouts.app')
@section('title','AI Asistan — Animexe')
@push('styles')
<style>
.ai-hero{
padding:52px 24px 28px;max-width:900px;margin:0 auto;text-align:center;
}
.ai-hero-icon{
width:80px;height:80px;border-radius:50%;margin:0 auto 20px;
background:linear-gradient(135deg,var(--accent3),var(--accent));
display:flex;align-items:center;justify-content:center;font-size:2.2rem;color:#fff;
box-shadow:0 0 48px rgba(184,77,255,.45),0 0 80px rgba(255,45,125,.2);
animation:ai-glow 3s ease-in-out infinite;
}
@keyframes ai-glow{
0%,100%{box-shadow:0 0 48px rgba(184,77,255,.45),0 0 80px rgba(255,45,125,.2)}
50%{box-shadow:0 0 64px rgba(184,77,255,.65),0 0 100px rgba(255,45,125,.35)}
}
.ai-hero h1{
font-family:var(--font-display);font-size:clamp(1.6rem,4vw,2.5rem);font-weight:800;
background:linear-gradient(120deg,#fff 0%,var(--accent3) 40%,var(--accent) 100%);
-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;
margin-bottom:10px;
}
.ai-hero p{color:var(--text2);font-size:1rem;max-width:520px;margin:0 auto 32px;line-height:1.7}
.ai-tabs{
display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin-bottom:32px;
}
.ai-tab{
padding:10px 22px;border-radius:999px;font-size:.88rem;font-weight:700;cursor:pointer;
background:var(--surface);border:1px solid var(--border);color:var(--text2);
transition:all .18s;
}
.ai-tab.on,.ai-tab:hover{
background:linear-gradient(135deg,rgba(184,77,255,.18),rgba(255,45,125,.1));
border-color:var(--accent3);color:var(--accent3);
box-shadow:0 0 20px rgba(184,77,255,.2);
}
.ai-section{max-width:900px;margin:0 auto;padding:0 24px 48px}
/* Öneri kartı */
.ai-box{
background:linear-gradient(145deg,rgba(17,17,31,.95),rgba(10,10,20,.98));
border:1px solid rgba(184,77,255,.2);border-radius:18px;padding:28px;
box-shadow:0 8px 40px rgba(0,0,0,.35),0 0 40px rgba(184,77,255,.05);
}
.ai-box-title{
font-family:var(--font-display);font-size:.85rem;letter-spacing:.12em;
text-transform:uppercase;color:var(--accent3);font-weight:700;
display:flex;align-items:center;gap:10px;margin-bottom:20px;
}
.ai-box-title i{font-size:1.1rem}
/* Ruh hali chips */
.mood-grid{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:20px}
.mood-chip{
padding:8px 16px;border-radius:999px;font-size:.82rem;font-weight:600;cursor:pointer;
background:rgba(17,17,31,.8);border:1px solid var(--border);color:var(--text2);
transition:all .16s;display:flex;align-items:center;gap:6px;
}
.mood-chip.on,.mood-chip:hover{
background:rgba(184,77,255,.14);border-color:var(--accent3);color:var(--accent3);
box-shadow:0 0 16px rgba(184,77,255,.2);
}
/* Tür chips */
.genre-wrap{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:20px}
/* Tip toggle */
.type-row{display:flex;gap:8px;margin-bottom:24px}
.type-btn{
flex:1;padding:10px;border-radius:10px;border:1px solid var(--border);
background:var(--surface);color:var(--text2);font-size:.84rem;font-weight:700;
cursor:pointer;transition:all .16s;text-align:center;
}
.type-btn.on{
background:rgba(0,245,255,.1);border-color:var(--accent2);color:var(--accent2);
box-shadow:0 0 16px rgba(0,245,255,.12);
}
.ai-btn{
width:100%;padding:14px;border-radius:12px;border:none;cursor:pointer;
background:linear-gradient(135deg,var(--accent3),var(--accent));color:#fff;
font-size:.95rem;font-weight:800;letter-spacing:.04em;
transition:transform .16s,box-shadow .18s;
box-shadow:0 0 28px rgba(184,77,255,.3),0 4px 16px rgba(255,45,125,.2);
display:flex;align-items:center;justify-content:center;gap:10px;
}
.ai-btn:hover{transform:translateY(-1px);box-shadow:0 0 40px rgba(184,77,255,.45)}
.ai-btn:disabled{opacity:.55;cursor:not-allowed;transform:none}
/* Loader */
.ai-loader{display:none;justify-content:center;align-items:center;gap:6px;padding:28px}
.ai-loader.on{display:flex}
.al-dot{width:10px;height:10px;border-radius:50%;background:var(--accent3);animation:al-b .9s ease-in-out infinite}
.al-dot:nth-child(2){animation-delay:.18s;background:var(--accent)}
.al-dot:nth-child(3){animation-delay:.36s;background:var(--accent2)}
@keyframes al-b{0%,80%,100%{transform:scale(.5);opacity:.4}40%{transform:scale(1);opacity:1}}
/* Sonuç grid */
.ai-results{margin-top:24px;display:none}
.ai-results.on{display:block}
.ai-res-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:16px;margin-top:16px}
.ai-res-card{
background:var(--surface);border:1px solid rgba(0,245,255,.1);border-radius:14px;
overflow:hidden;cursor:pointer;transition:all .2s;
}
.ai-res-card:hover{transform:translateY(-5px);border-color:rgba(255,45,125,.4);box-shadow:0 12px 36px rgba(0,0,0,.4),0 0 28px rgba(255,45,125,.1)}
.ai-res-poster{position:relative;aspect-ratio:2/3;background:var(--surface2);overflow:hidden}
.ai-res-poster img{width:100%;height:100%;object-fit:cover;transition:transform .28s}
.ai-res-card:hover .ai-res-poster img{transform:scale(1.06)}
.ai-res-poster-ph{width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:var(--text3);font-size:2rem}
.ai-res-reason{
position:absolute;bottom:0;left:0;right:0;padding:8px;
background:linear-gradient(to top,rgba(0,0,0,.88),transparent);
font-size:.65rem;color:rgba(255,255,255,.8);line-height:1.35;
}
.ai-res-body{padding:9px 10px}
.ai-res-title{font-size:.8rem;font-weight:700;color:var(--text);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.ai-res-meta{font-size:.7rem;color:var(--text3);margin-top:3px}
/* Natural search */
.ai-search-wrap{position:relative}
.ai-search-wrap input{
width:100%;background:rgba(255,255,255,.05);border:1px solid rgba(184,77,255,.3);
border-radius:12px;padding:14px 52px 14px 18px;color:var(--text);font-size:.9rem;
font-family:inherit;outline:none;transition:all .18s;
}
.ai-search-wrap input:focus{
border-color:var(--accent3);background:rgba(184,77,255,.06);
box-shadow:0 0 0 3px rgba(184,77,255,.15),0 0 32px rgba(184,77,255,.1);
}
.ai-search-wrap input::placeholder{color:var(--text3)}
.ai-search-btn{
position:absolute;right:10px;top:50%;transform:translateY(-50%);
width:36px;height:36px;border-radius:9px;border:none;cursor:pointer;
background:linear-gradient(135deg,var(--accent3),var(--accent));color:#fff;font-size:.9rem;
display:flex;align-items:center;justify-content:center;transition:transform .14s;
}
.ai-search-btn:hover{transform:translateY(-50%) scale(1.08)}
.ai-search-hints{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}
.ai-hint{
padding:6px 13px;border-radius:999px;font-size:.75rem;font-weight:600;cursor:pointer;
background:rgba(0,245,255,.06);border:1px solid rgba(0,245,255,.2);color:var(--accent2);
transition:all .14s;
}
.ai-hint:hover{background:rgba(0,245,255,.12);box-shadow:0 0 12px rgba(0,245,255,.15)}
.ai-empty{text-align:center;padding:32px;color:var(--text3);font-size:.9rem}
.ai-error{padding:14px 18px;border-radius:10px;background:rgba(255,60,90,.08);border:1px solid rgba(255,60,90,.25);color:#ff8a9a;font-size:.85rem;margin-top:16px;display:none}
.ai-error.on{display:block}
@media(max-width:600px){
.ai-hero{padding:36px 16px 20px}
.ai-hero p{font-size:.9rem}
.ai-section{padding:0 14px 40px}
.ai-box{padding:18px 14px}
.ai-res-grid{grid-template-columns:repeat(3,1fr);gap:10px}
.ai-tabs{gap:5px}
.ai-tab{padding:8px 14px;font-size:.82rem}
.type-row{gap:6px}
.mood-chip{font-size:.78rem;padding:6px 12px}
.ai-search-wrap input{font-size:.85rem;padding:12px 46px 12px 14px}
}
@media(max-width:380px){
.ai-res-grid{grid-template-columns:repeat(2,1fr)}
.ai-hero h1{font-size:1.4rem}
}
</style>
@endpush
@section('content')
<div class="ai-hero">
<div class="ai-hero-icon"><i class="bi bi-stars"></i></div>
<h1>Animexe AI Asistan</h1>
<p>Yapay zeka destekli anime keşfi. Ruh haline göre öneri al, doğal dille ara veya sağ alttaki balondan sohbet et.</p>
@guest
<div style="background:rgba(255,45,125,.08);border:1px solid rgba(255,45,125,.25);border-radius:14px;padding:20px 24px;text-align:center;max-width:480px;margin:20px auto 0">
<i class="bi bi-lock" style="font-size:1.8rem;color:var(--accent);display:block;margin-bottom:10px"></i>
<p style="color:var(--text2);font-size:.9rem;margin-bottom:16px">AI özelliklerini kullanmak için ücretsiz hesap oluştur veya giriş yap.</p>
<a href="{{ route('frontend.register') }}" style="display:inline-block;padding:10px 24px;background:var(--accent);border-radius:20px;color:#fff;font-weight:700;text-decoration:none;margin-right:8px">Ücretsiz Kayıt Ol</a>
<a href="{{ route('frontend.login') }}" style="display:inline-block;padding:10px 24px;background:#21262d;border-radius:20px;color:var(--text2);font-weight:600;text-decoration:none">Giriş Yap</a>
</div>
@endguest
<div class="ai-tabs" @guest style="margin-top:20px;opacity:.5;pointer-events:none" @endguest>
<button class="ai-tab on" onclick="switchAiTab('recommend',this)"><i class="bi bi-magic"></i> Kişisel Öneri</button>
<button class="ai-tab" onclick="switchAiTab('search',this)"><i class="bi bi-search-heart"></i> Doğal Dil Ara</button>
<button class="ai-tab" onclick="switchAiTab('platform',this)"><i class="bi bi-question-circle"></i> Site Yardımı</button>
</div>
</div>
<div class="ai-section">
{{-- ── TAB 1: Kişisel Öneri ─────────────────────────────────── --}}
<div id="tab-recommend">
<div class="ai-box">
<div class="ai-box-title"><i class="bi bi-emoji-smile"></i> Nasıl bir ruh halindeydin?</div>
<div class="mood-grid">
@foreach([
['😤','Aksiyona dolu','action'],['😢','Duygusal ağlatıcı','emotional'],
['😂','Komik ve eğlenceli','comedy'],['😨','Gerilim/Korku','thriller'],
['🌸','Romantik','romance'],['🤯','Akıl oyunları','mindgame'],
['⚔️','Fantezi macera','fantasy'],['🤖','Sci-Fi / Mecha','scifi'],
['🏫','Okul / Slice of life','school'],['💪','Motivasyonel','motivational'],
] as $m)
<button class="mood-chip" data-mood="{{ $m[2] }}" onclick="toggleMood(this,'{{ addslashes($m[0].' '.$m[1]) }}')">
{{ $m[0] }} {{ $m[1] }}
</button>
@endforeach
</div>
<div class="ai-box-title" style="margin-top:4px"><i class="bi bi-tags"></i> Tür tercihleri (opsiyonel)</div>
<div class="genre-wrap" id="genre-wrap">
@foreach($genres as $g)
<button class="genre-chip" data-genre="{{ $g->name }}" onclick="this.classList.toggle('on')">{{ $g->name }}</button>
@endforeach
</div>
<div class="ai-box-title" style="margin-top:4px"><i class="bi bi-collection-play"></i> İçerik tipi</div>
<div class="type-row" id="type-row">
<button class="type-btn on" data-type="" onclick="selectType(this)">Hepsi</button>
<button class="type-btn" data-type="series" onclick="selectType(this)">Dizi</button>
<button class="type-btn" data-type="movie" onclick="selectType(this)">Film</button>
</div>
<button class="ai-btn" id="rec-btn" onclick="getRecommendations()">
<i class="bi bi-magic"></i> Bana Özel Öneri Al
</button>
<div class="ai-error" id="rec-error"></div>
</div>
<div class="ai-loader" id="rec-loader">
<div class="al-dot"></div><div class="al-dot"></div><div class="al-dot"></div>
<span style="color:var(--text3);font-size:.85rem;margin-left:6px">AI düşünüyor...</span>
</div>
<div class="ai-results" id="rec-results">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:4px">
<span style="font-family:var(--font-display);font-size:.82rem;letter-spacing:.1em;text-transform:uppercase;color:var(--accent3)"><i class="bi bi-stars" style="margin-right:6px"></i>AI Önerileri</span>
</div>
<div class="ai-res-grid" id="rec-grid"></div>
</div>
</div>
{{-- ── TAB 3: Platform Yardımı ─────────────────────────────── --}}
<div id="tab-platform" style="display:none">
<div class="ai-box">
<div class="ai-box-title"><i class="bi bi-question-circle"></i> Animexe Hakkında Sık Sorulanlar</div>
<div style="display:grid;gap:10px">
@foreach([
['Kayıt olmak ücretsiz mi?','Evet! Animexe\'ye kayıt olmak tamamen ücretsizdir. E-posta adresinle saniyeler içinde hesap oluşturabilirsin.'],
['Premium üyelik ne sağlar?','Premium üyelik ile reklamsız izleme, 1080p HD kalite ve yeni bölümlere erken erişim imkanı kazanırsın.'],
['Altyazı ve dublaj var mı?','Evet, çoğu animede Türkçe altyazı bulunur. Bir kısım animede Türkçe dublaj da mevcuttur.'],
['Hangi cihazlarda çalışır?','Bilgisayar, tablet ve telefon tarayıcısında sorunsuz çalışır. Özel bir uygulama indirmene gerek yok.'],
['Şifremi unuttum, ne yapmalıyım?','Giriş sayfasındaki "Şifremi Unuttum" linkine tıklayarak e-postanla şifre sıfırlama bağlantısı alabilirsin.'],
] as [$q, $a])
<details style="background:rgba(255,255,255,.03);border:1px solid rgba(184,77,255,.15);border-radius:10px;overflow:hidden">
<summary style="padding:13px 16px;cursor:pointer;font-size:.86rem;font-weight:600;color:var(--text);list-style:none;display:flex;align-items:center;justify-content:space-between">
{{ $q }}
<i class="bi bi-chevron-down" style="font-size:.75rem;color:var(--accent3);flex-shrink:0;transition:transform .2s"></i>
</summary>
<p style="padding:0 16px 14px;margin:0;font-size:.84rem;color:var(--text2);line-height:1.65">{{ $a }}</p>
</details>
@endforeach
</div>
<div style="margin-top:20px;padding:14px;background:rgba(184,77,255,.07);border:1px solid rgba(184,77,255,.2);border-radius:12px;text-align:center">
<p style="margin:0 0 12px;color:var(--text2);font-size:.85rem">Cevabını bulamadın mı? AI asistana sor!</p>
<button class="ai-btn" style="width:auto;padding:10px 24px;display:inline-flex" onclick="openAiWithQuestion()">
<i class="bi bi-chat-dots-fill"></i> AI\'a Sor
</button>
</div>
</div>
</div>
{{-- ── TAB 2: Doğal Dil Arama ─────────────────────────────── --}}
<div id="tab-search" style="display:none">
<div class="ai-box">
<div class="ai-box-title"><i class="bi bi-search-heart"></i> Ne aramak istiyorsun?</div>
<div class="ai-search-wrap">
<input type="text" id="ai-search-inp" placeholder="Ör: ağlatan romantik okul animesi, 2023'te çıkmış aksiyon dizisi..."
onkeydown="if(event.key==='Enter') aiNaturalSearch()">
<button class="ai-search-btn" onclick="aiNaturalSearch()"><i class="bi bi-send-fill"></i></button>
</div>
<div class="ai-search-hints">
@foreach(['Ağlatan romantik anime','İsekai macera dizisi','Kısa 12 bölüm aksiyon','Dövüş sanatları anime','Distopik sci-fi film','Komedi okul dizisi'] as $h)
<button class="ai-hint" onclick="document.getElementById('ai-search-inp').value=this.textContent;aiNaturalSearch()">{{ $h }}</button>
@endforeach
</div>
<div class="ai-error" id="search-error"></div>
</div>
<div class="ai-loader" id="search-loader">
<div class="al-dot"></div><div class="al-dot"></div><div class="al-dot"></div>
<span style="color:var(--text3);font-size:.85rem;margin-left:6px">AI arıyor...</span>
</div>
<div class="ai-results" id="search-results">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:4px">
<span style="font-family:var(--font-display);font-size:.82rem;letter-spacing:.1em;text-transform:uppercase;color:var(--accent2)"><i class="bi bi-lightning-fill" style="margin-right:6px"></i>AI Arama Sonuçları</span>
</div>
<div class="ai-res-grid" id="search-grid"></div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
const CSRF = document.querySelector('meta[name=csrf-token]').content;
// ── Tab yönetimi ──────────────────────────────────────────────────────────────
function switchAiTab(tab, btn){
document.querySelectorAll('.ai-tab').forEach(b=>b.classList.remove('on'));
btn.classList.add('on');
document.getElementById('tab-recommend').style.display = tab==='recommend' ? '' : 'none';
document.getElementById('tab-search').style.display = tab==='search' ? '' : 'none';
document.getElementById('tab-platform').style.display = tab==='platform' ? '' : 'none';
}
function openAiWithQuestion(){
if(typeof window.toggleAiPanel === 'function'){
window.toggleAiPanel();
setTimeout(()=>{
const inp = document.getElementById('ai-inp');
if(inp){ inp.focus(); inp.placeholder='Site hakkında soru sor...'; }
}, 300);
}
}
// ── Ruh hali seçimi ───────────────────────────────────────────────────────────
let selectedMoods = [];
function toggleMood(btn, label){
btn.classList.toggle('on');
if(btn.classList.contains('on')){
selectedMoods.push(label);
} else {
selectedMoods = selectedMoods.filter(m=>m!==label);
}
}
function selectType(btn){
document.querySelectorAll('#type-row .type-btn').forEach(b=>b.classList.remove('on'));
btn.classList.add('on');
}
// ── Öneri al ─────────────────────────────────────────────────────────────────
async function getRecommendations(){
const btn = document.getElementById('rec-btn');
const loader = document.getElementById('rec-loader');
const results = document.getElementById('rec-results');
const errEl = document.getElementById('rec-error');
const mood = selectedMoods.join(', ');
const genres = [...document.querySelectorAll('#genre-wrap .genre-chip.on')].map(c=>c.dataset.genre);
const type = document.querySelector('#type-row .type-btn.on')?.dataset.type || '';
btn.disabled = true;
loader.classList.add('on');
results.classList.remove('on');
errEl.classList.remove('on');
try{
const res = await fetch('{{ route("ai.recommend") }}',{
method:'POST',
headers:{'Content-Type':'application/json','X-CSRF-TOKEN':CSRF},
body: JSON.stringify({ mood, genres, type })
});
const data = await res.json();
loader.classList.remove('on');
if(data.error){ errEl.textContent=data.error; errEl.classList.add('on'); }
else { renderRecGrid(data.recommendations || [], 'rec-grid', true); results.classList.add('on'); }
}catch(e){
loader.classList.remove('on');
errEl.textContent = 'Bağlantı hatası, tekrar dene.';
errEl.classList.add('on');
}
btn.disabled = false;
}
// ── Doğal dil arama ───────────────────────────────────────────────────────────
async function aiNaturalSearch(){
const query = document.getElementById('ai-search-inp').value.trim();
if(!query) return;
const loader = document.getElementById('search-loader');
const results = document.getElementById('search-results');
const errEl = document.getElementById('search-error');
loader.classList.add('on');
results.classList.remove('on');
errEl.classList.remove('on');
try{
const res = await fetch('{{ route("ai.search") }}',{
method:'POST',
headers:{'Content-Type':'application/json','X-CSRF-TOKEN':CSRF},
body: JSON.stringify({ query })
});
const data = await res.json();
loader.classList.remove('on');
if(data.error){ errEl.textContent=data.error; errEl.classList.add('on'); }
else if(!data.results || !data.results.length){
errEl.textContent = 'Sonuç bulunamadı. Farklı bir şey dene!';
errEl.classList.add('on');
} else {
renderRecGrid(data.results, 'search-grid', false);
results.classList.add('on');
}
}catch(e){
loader.classList.remove('on');
errEl.textContent = 'Bağlantı hatası, tekrar dene.';
errEl.classList.add('on');
}
}
// ── Kart render ───────────────────────────────────────────────────────────────
function renderRecGrid(items, gridId, showReason){
const grid = document.getElementById(gridId);
grid.innerHTML = '';
if(!items.length){
grid.innerHTML = '<div class="ai-empty">Sonuç bulunamadı.</div>';
return;
}
items.forEach(a=>{
const detailUrl = '/anime/'+a.slug;
const watchUrl = '/watch/'+a.slug+'/1/1';
const typeLabel = a.type==='movie' ? '🎬 Film' : (a.type==='ova' ? 'OVA' : '📺 Dizi');
const ratingHtml = a.rating ? `<span style="color:#f59e0b;font-weight:700">★${parseFloat(a.rating).toFixed(1)}</span>` : '';
const epHtml = a.episode_count ? `<span>${a.episode_count} bölüm</span>` : '';
grid.innerHTML += `
<div class="ai-res-card">
<div class="ai-res-poster">
${a.cover
? `<img src="${esc(a.cover)}" alt="${esc(a.title)}" loading="lazy">`
: `<div class="ai-res-poster-ph"><i class="bi bi-image"></i></div>`}
<div style="position:absolute;top:6px;left:6px;background:rgba(0,0,0,.72);backdrop-filter:blur(4px);border-radius:5px;padding:2px 7px;font-size:.65rem;font-weight:700;color:#fff">${typeLabel}</div>
${showReason && a.reason ? `<div class="ai-res-reason">${esc(a.reason)}</div>` : ''}
</div>
<div class="ai-res-body">
<div class="ai-res-title">${esc(a.title)}</div>
<div class="ai-res-meta" style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:4px">
${ratingHtml}${epHtml}
</div>
</div>
<div style="display:flex;gap:5px;padding:0 9px 9px">
<a href="${esc(watchUrl)}" style="flex:1;padding:6px 0;background:linear-gradient(135deg,var(--accent3),var(--accent));border-radius:8px;color:#fff;font-size:.72rem;font-weight:700;text-align:center;text-decoration:none;display:flex;align-items:center;justify-content:center;gap:3px"><i class="bi bi-play-fill"></i>İzle</a>
<a href="${esc(detailUrl)}" style="flex:1;padding:6px 0;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);border-radius:8px;color:var(--text2);font-size:.72rem;font-weight:700;text-align:center;text-decoration:none;display:flex;align-items:center;justify-content:center;gap:3px"><i class="bi bi-info-circle"></i>Detay</a>
</div>
</div>`;
});
}
function esc(s){ return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
</script>
@endpush
@@ -0,0 +1,213 @@
@extends('frontend.layouts.app')
@section('title', 'Anime İsteği — Animexe')
@push('styles')
<style>
.req-hero{padding:60px 24px 32px;text-align:center;max-width:700px;margin:0 auto}
.req-hero h1{font-size:2rem;font-weight:800;color:var(--text);margin-bottom:8px}
.req-hero p{color:var(--text2);font-size:.95rem}
.req-wrap{max-width:900px;margin:0 auto;padding:0 20px 60px}
.req-form-card{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:24px;margin-bottom:32px}
.req-form-card h2{font-size:1rem;font-weight:700;color:var(--text);margin-bottom:18px;display:flex;align-items:center;gap:8px}
.rf-field{margin-bottom:16px}
.rf-label{display:block;font-size:.8rem;color:var(--text2);margin-bottom:6px;font-weight:500}
.rf-input{width:100%;background:#0a0a14;border:1px solid var(--border);border-radius:8px;padding:10px 14px;color:var(--text);font-size:.9rem;transition:border-color .2s}
.rf-input:focus{outline:none;border-color:var(--accent)}
.rf-textarea{resize:vertical;min-height:80px}
.rf-submit{display:flex;align-items:center;justify-content:center;gap:8px;padding:11px 24px;background:var(--accent);border:none;border-radius:20px;color:#fff;font-size:.9rem;font-weight:600;cursor:pointer;transition:opacity .2s}
.rf-submit:hover{opacity:.85}
.req-list-card{background:var(--surface);border:1px solid var(--border);border-radius:16px;overflow:hidden}
.req-list-head{padding:16px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
.req-list-head h2{font-size:.95rem;font-weight:700;color:var(--text);margin:0}
.req-item{padding:16px 20px;border-bottom:1px solid rgba(255,255,255,.04);display:flex;align-items:flex-start;gap:16px;transition:background .15s}
.req-item:last-child{border-bottom:none}
.req-item:hover{background:rgba(255,255,255,.02)}
.req-vote{display:flex;flex-direction:column;align-items:center;gap:4px;min-width:52px}
.req-vote-btn{width:44px;height:44px;border-radius:50%;border:1px solid var(--border);background:transparent;color:var(--text3);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;font-size:.95rem}
.req-vote-btn:hover,.req-vote-btn.voted{border-color:var(--accent);background:rgba(255,45,125,.12);color:var(--accent)}
.req-vote-cnt{font-size:.82rem;font-weight:700;color:var(--text2)}
.req-body{flex:1;min-width:0}
.req-title{font-size:.95rem;font-weight:600;color:var(--text);margin-bottom:3px}
.req-ori{font-size:.78rem;color:var(--text3);margin-bottom:6px}
.req-note{font-size:.78rem;color:var(--text2);margin-bottom:8px;line-height:1.5}
.req-meta{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.req-status{font-size:.7rem;padding:2px 10px;border-radius:20px;font-weight:600}
.req-by{font-size:.72rem;color:var(--text3)}
.req-admin-note{font-size:.75rem;color:#f0883e;margin-top:6px;display:flex;align-items:center;gap:5px}
.toast-msg{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:#161b22;border:1px solid #30363d;border-radius:10px;padding:12px 20px;color:var(--text);font-size:.85rem;z-index:9999;animation:fadeInUp .3s ease;box-shadow:0 8px 24px rgba(0,0,0,.5)}
@keyframes fadeInUp{from{transform:translateX(-50%) translateY(10px);opacity:0}to{transform:translateX(-50%) translateY(0);opacity:1}}
</style>
@endpush
@section('content')
<div style="padding-top:var(--nav-h)">
<div class="req-hero">
<h1><i class="bi bi-plus-circle" style="color:var(--accent)"></i> Anime İsteği</h1>
<p>İzlemek istediğin ama sitede olmayan bir anime var mı? Buradan bildir, en çok istenen animeleri ekliyoruz.</p>
</div>
<div class="req-wrap">
{{-- İstek formu (sadece üyeler) --}}
@auth
<div class="req-form-card" id="req-form-card">
<h2><i class="bi bi-send" style="color:var(--accent)"></i> Yeni İstek Gönder</h2>
<div id="req-success" style="display:none;background:rgba(63,185,80,.1);border:1px solid #3fb95040;border-radius:8px;padding:12px 16px;color:#3fb950;font-size:.88rem;margin-bottom:16px"></div>
<div class="rf-field">
<label class="rf-label">Anime Adı (Türkçe veya bilinen ismi) *</label>
<input id="rf-title" type="text" class="rf-input" placeholder="örn: Demon Slayer" maxlength="200">
</div>
<div class="rf-field">
<label class="rf-label">Orijinal / İngilizce Adı</label>
<input id="rf-ori" type="text" class="rf-input" placeholder="örn: Kimetsu no Yaiba" maxlength="200">
</div>
<div class="rf-field">
<label class="rf-label">Not / Açıklama</label>
<textarea id="rf-note" class="rf-input rf-textarea" placeholder="Hangi sezon, neden istiyorsun? vs."></textarea>
</div>
<button class="rf-submit" id="rf-btn" onclick="submitRequest()">
<i class="bi bi-send-fill"></i> İstek Gönder
</button>
</div>
@else
<div style="background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:28px;text-align:center;margin-bottom:32px">
<i class="bi bi-lock" style="font-size:2rem;color:var(--text3);margin-bottom:12px;display:block"></i>
<p style="color:var(--text2);margin-bottom:16px">Anime isteği göndermek için giriş yapman gerekiyor.</p>
<a href="{{ route('frontend.login') }}" style="padding:10px 24px;background:var(--accent);border-radius:20px;color:#fff;font-weight:600;text-decoration:none;font-size:.88rem">Giriş Yap</a>
<a href="{{ route('frontend.register') }}" style="padding:10px 24px;background:#21262d;border-radius:20px;color:var(--text2);font-weight:600;text-decoration:none;font-size:.88rem;margin-left:8px">Kayıt Ol</a>
</div>
@endauth
{{-- İstekler listesi --}}
<div class="req-list-card">
<div class="req-list-head">
<h2><i class="bi bi-list-ul" style="color:var(--accent);margin-right:6px"></i>Tüm İstekler</h2>
<span style="font-size:.78rem;color:var(--text3)">{{ $requests->total() }} istek</span>
</div>
@forelse($requests as $req)
@php
$st = \App\Models\AnimeRequest::STATUSES[$req->status] ?? ['label'=>$req->status,'color'=>'#8b949e'];
$hasVoted = in_array($req->id, $votedIds);
@endphp
<div class="req-item">
<div class="req-vote">
@auth
<button class="req-vote-btn {{ $hasVoted?'voted':'' }}" id="vote-btn-{{ $req->id }}" onclick="voteRequest({{ $req->id }})">
<i class="bi bi-heart{{ $hasVoted?'-fill':'' }}" id="vote-icon-{{ $req->id }}"></i>
</button>
@else
<a href="{{ route('frontend.login') }}" class="req-vote-btn"><i class="bi bi-heart"></i></a>
@endauth
<span class="req-vote-cnt" id="vote-cnt-{{ $req->id }}">{{ $req->vote_count }}</span>
</div>
<div class="req-body">
<div class="req-title">{{ $req->title }}</div>
@if($req->original_title)<div class="req-ori">{{ $req->original_title }}</div>@endif
@if($req->note)<div class="req-note">{{ Str::limit($req->note, 150) }}</div>@endif
<div class="req-meta">
<span class="req-status" style="background:{{ $st['color'] }}22;color:{{ $st['color'] }};border:1px solid {{ $st['color'] }}44">{{ $st['label'] }}</span>
@if($req->user)<span class="req-by">{{ $req->user->name }} tarafından istendi</span>@endif
<span class="req-by">· {{ $req->created_at->diffForHumans() }}</span>
</div>
@if($req->admin_note)
<div class="req-admin-note"><i class="bi bi-shield-check"></i> {{ $req->admin_note }}</div>
@endif
</div>
</div>
@empty
<div style="padding:40px;text-align:center;color:var(--text3)">Henüz istek yok. İlk isteği sen gönder!</div>
@endforelse
</div>
<div class="mt-4">{{ $requests->links() }}</div>
</div>
</div>
@endsection
@push('scripts')
<script>
const CSRF = document.querySelector('meta[name=csrf-token]')?.content||'';
async function submitRequest() {
const title = document.getElementById('rf-title').value.trim();
if (!title) { showToast('Anime adı zorunlu!'); return; }
const btn = document.getElementById('rf-btn');
btn.disabled = true;
btn.innerHTML = '<i class="bi bi-hourglass-split"></i> Gönderiliyor...';
try {
const res = await fetch('{{ route("anime.request.store") }}', {
method:'POST',
headers:{'Content-Type':'application/json','X-CSRF-TOKEN':CSRF},
body: JSON.stringify({
title,
original_title: document.getElementById('rf-ori').value.trim(),
note: document.getElementById('rf-note').value.trim(),
})
});
const d = await res.json();
const msg = d.merged
? `Bu anime zaten istekler listesinde! Oyun eklendi (toplam: ${d.vote_count} oy).`
: `İsteğin gönderildi! Teşekkürler 🎉`;
document.getElementById('req-success').style.display = 'block';
document.getElementById('req-success').textContent = msg;
document.getElementById('rf-title').value = '';
document.getElementById('rf-ori').value = '';
document.getElementById('rf-note').value = '';
setTimeout(()=>location.reload(), 2000);
} catch(e) {
showToast('Bir hata oluştu, tekrar dene.');
}
btn.disabled = false;
btn.innerHTML = '<i class="bi bi-send-fill"></i> İstek Gönder';
}
async function voteRequest(id) {
const btn = document.getElementById(`vote-btn-${id}`);
const ico = document.getElementById(`vote-icon-${id}`);
if (btn) btn.disabled = true;
try {
const res = await fetch(`/anime-request/${id}/vote`, {
method:'POST',
headers:{'Content-Type':'application/json','X-CSRF-TOKEN':CSRF},
body:'{}'
});
if (res.status === 401) { window.location = '{{ route("frontend.login") }}'; return; }
if (!res.ok) { showToast('Bir hata oluştu.'); return; }
const d = await res.json();
document.getElementById(`vote-cnt-${id}`).textContent = d.vote_count;
if (d.voted) {
btn.classList.add('voted');
ico.className = 'bi bi-heart-fill';
} else {
btn.classList.remove('voted');
ico.className = 'bi bi-heart';
}
} catch(e) {
showToast('Bağlantı hatası, tekrar dene.');
}
if (btn) btn.disabled = false;
}
function showToast(msg) {
const t = document.createElement('div');
t.className='toast-msg';
t.textContent=msg;
document.body.appendChild(t);
setTimeout(()=>t.remove(), 3000);
}
</script>
@endpush
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,57 @@
@extends('frontend.layouts.app')
@section('title','Şifremi Unuttum — Animexe')
@push('styles')
<style>
.auth-page{min-height:calc(100vh - var(--nav-h));display:flex;align-items:center;justify-content:center;padding:40px 16px;position:relative;overflow:hidden;background:radial-gradient(ellipse 80% 60% at 50% -20%,rgba(184,77,255,.2),transparent 55%),radial-gradient(ellipse 50% 40% at 100% 100%,rgba(0,245,255,.1),transparent)}
.auth-page::before{content:'';position:absolute;inset:0;pointer-events:none;background:linear-gradient(rgba(0,245,255,.03) 1px,transparent 1px),linear-gradient(90deg,rgba(0,245,255,.03) 1px,transparent 1px);background-size:40px 40px;mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black,transparent)}
@keyframes auth-box-in{from{opacity:0;transform:translateY(24px) scale(.97)}to{opacity:1;transform:none}}
.auth-box{background:linear-gradient(160deg,rgba(14,14,28,.97),rgba(8,8,18,.99));border:1px solid rgba(0,245,255,.22);border-radius:20px;padding:clamp(28px,5vw,44px);width:100%;max-width:440px;box-shadow:0 32px 80px rgba(0,0,0,.65),0 0 60px rgba(0,245,255,.08);position:relative;overflow:hidden;animation:auth-box-in .55s cubic-bezier(.22,1,.36,1) .1s both}
.auth-box::before{content:'';position:absolute;top:0;left:10%;right:10%;height:1px;background:linear-gradient(90deg,transparent,var(--accent2),var(--accent),transparent);opacity:.7}
.auth-logo{text-align:center;margin-bottom:28px}
.auth-logo .name{font-family:var(--font-display);font-size:1.4rem;font-weight:900;letter-spacing:.22em;background:linear-gradient(105deg,#fff,var(--accent2),var(--accent));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;filter:drop-shadow(0 0 20px rgba(0,245,255,.3))}
.auth-logo p{color:rgba(255,255,255,.35);font-size:.85rem;margin-top:5px}
.f-group{margin-bottom:16px}
.f-group label{display:block;font-size:.79rem;font-weight:700;color:rgba(255,255,255,.45);margin-bottom:7px;letter-spacing:.05em;text-transform:uppercase}
.f-group input{width:100%;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:12px 16px;color:var(--text);font-size:.9rem;outline:none;transition:border .18s,box-shadow .18s}
.f-group input:focus{border-color:var(--accent2);box-shadow:0 0 0 2px rgba(0,245,255,.12);background:rgba(0,245,255,.04)}
.f-group input::placeholder{color:rgba(255,255,255,.2)}
.auth-btn{width:100%;padding:13px;border-radius:12px;border:none;cursor:pointer;background:linear-gradient(135deg,var(--accent),var(--accent3));color:#fff;font-size:.93rem;font-weight:800;transition:transform .14s,box-shadow .18s;box-shadow:0 0 32px rgba(255,45,125,.35),0 4px 20px rgba(0,0,0,.35)}
.auth-btn:hover{transform:translateY(-2px);box-shadow:0 0 44px rgba(255,45,125,.5)}
.auth-switch{text-align:center;font-size:.85rem;color:rgba(255,255,255,.35);margin-top:20px}
.auth-switch a{color:var(--accent2);font-weight:700}
.status-box{background:rgba(0,245,255,.07);border:1px solid rgba(0,245,255,.2);border-radius:10px;padding:14px 16px;font-size:.87rem;color:var(--accent2);margin-bottom:20px}
</style>
@endpush
@section('content')
<div class="auth-page">
<div class="auth-box">
<div class="auth-logo">
<div class="name">ANIMEXE</div>
<p>Şifre sıfırlama</p>
</div>
@if(session('status'))
<div class="status-box"><i class="bi bi-check-circle-fill"></i> {{ session('status') }}</div>
@endif
@if($errors->any())
<div class="alert alert-err">{{ $errors->first() }}</div>
@endif
<p style="font-size:.87rem;color:rgba(255,255,255,.4);margin-bottom:20px">Kayıtlı e-posta adresini gir, sana şifre sıfırlama bağlantısı gönderelim.</p>
<form action="{{ route('password.request') }}" method="POST">
@csrf
<div class="f-group">
<label>E-posta Adresi</label>
<input type="email" name="email" value="{{ old('email') }}" placeholder="ornek@email.com" required autofocus>
</div>
<button type="submit" class="auth-btn">Sıfırlama Bağlantısı Gönder</button>
</form>
<div class="auth-switch">
<a href="{{ route('frontend.login') }}"><i class="bi bi-arrow-left"></i> Girişe dön</a>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,159 @@
@extends('frontend.layouts.app')
@section('title','Giriş Yap — Animexe')
@push('styles')
<style>
/* ═══════════════════════════════════════════════════
AUTH PAGE PREMIUM
═══════════════════════════════════════════════════ */
.auth-page{
min-height:calc(100vh - var(--nav-h));
display:flex;align-items:center;justify-content:center;
padding:40px 16px;
position:relative;overflow:hidden;
background:
radial-gradient(ellipse 80% 60% at 50% -20%,rgba(184,77,255,.2),transparent 55%),
radial-gradient(ellipse 50% 40% at 100% 100%,rgba(0,245,255,.1),transparent),
radial-gradient(ellipse 40% 35% at 0% 100%,rgba(255,45,125,.08),transparent);
}
/* Animated grid lines in background */
.auth-page::before{
content:'';position:absolute;inset:0;pointer-events:none;
background:
linear-gradient(rgba(0,245,255,.03) 1px,transparent 1px),
linear-gradient(90deg,rgba(0,245,255,.03) 1px,transparent 1px);
background-size:40px 40px;
mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black,transparent);
}
@keyframes auth-box-in{
from{opacity:0;transform:translateY(24px) scale(.97)}
to{opacity:1;transform:none}
}
.auth-box{
background:linear-gradient(160deg,rgba(14,14,28,.97),rgba(8,8,18,.99));
border:1px solid rgba(0,245,255,.22);
border-radius:20px;padding:clamp(28px,5vw,44px);
width:100%;max-width:440px;
box-shadow:0 32px 80px rgba(0,0,0,.65),0 0 60px rgba(0,245,255,.08),0 0 80px rgba(255,45,125,.05);
position:relative;overflow:hidden;
animation:auth-box-in .55s cubic-bezier(.22,1,.36,1) .1s both;
}
/* Top accent line */
.auth-box::before{
content:'';position:absolute;top:0;left:10%;right:10%;height:1px;
background:linear-gradient(90deg,transparent,var(--accent2),var(--accent),transparent);
opacity:.7;
}
.auth-logo{text-align:center;margin-bottom:28px}
.auth-logo .name{
font-family:var(--font-display);font-size:1.4rem;font-weight:900;letter-spacing:.22em;
background:linear-gradient(105deg,#fff 0%,var(--accent2) 40%,var(--accent) 100%);
-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;
text-shadow:none;
filter:drop-shadow(0 0 20px rgba(0,245,255,.3));
}
.auth-logo p{color:rgba(255,255,255,.35);font-size:.85rem;margin-top:5px}
.auth-title{font-size:1.18rem;font-weight:800;margin-bottom:22px;color:#fff}
.f-group{margin-bottom:16px;position:relative}
.f-group label{display:block;font-size:.79rem;font-weight:700;color:rgba(255,255,255,.45);margin-bottom:7px;letter-spacing:.05em;text-transform:uppercase}
.f-group input{
width:100%;
background:rgba(255,255,255,.04);
border:1px solid rgba(255,255,255,.1);
border-radius:10px;
padding:12px 16px;
color:var(--text);font-size:.9rem;outline:none;
transition:border .18s,box-shadow .18s,background .18s;
}
.f-group input:focus{
border-color:var(--accent2);
box-shadow:0 0 0 2px rgba(0,245,255,.12),0 0 24px rgba(0,245,255,.06);
background:rgba(0,245,255,.04);
}
.f-group input::placeholder{color:rgba(255,255,255,.2)}
.f-err{font-size:.78rem;color:#fca5a5;margin-top:5px;display:flex;align-items:center;gap:4px}
.f-check{display:flex;align-items:center;gap:9px;margin-bottom:18px}
.f-check input{width:16px;height:16px;accent-color:var(--accent);border-radius:4px}
.f-check label{font-size:.84rem;color:rgba(255,255,255,.45);cursor:pointer}
.auth-btn{
width:100%;padding:13px;border-radius:12px;border:none;cursor:pointer;
background:linear-gradient(135deg,var(--accent) 0%,var(--accent3) 100%);
color:#fff;font-size:.93rem;font-weight:800;letter-spacing:.03em;
transition:transform .14s,box-shadow .18s;
box-shadow:0 0 32px rgba(255,45,125,.35),0 4px 20px rgba(0,0,0,.35);
position:relative;overflow:hidden;
}
.auth-btn:hover{transform:translateY(-2px);box-shadow:0 0 44px rgba(255,45,125,.5),0 6px 24px rgba(0,0,0,.35)}
.auth-btn:active{transform:translateY(0)}
.auth-sep{text-align:center;margin:18px 0;color:rgba(255,255,255,.2);font-size:.8rem;position:relative}
.auth-sep::before,.auth-sep::after{content:'';position:absolute;top:50%;width:38%;height:1px;background:rgba(255,255,255,.08)}
.auth-sep::before{left:0}
.auth-sep::after{right:0}
.auth-switch{text-align:center;font-size:.85rem;color:rgba(255,255,255,.35);margin-top:20px}
.auth-switch a{color:var(--accent2);font-weight:700}
.auth-switch a:hover{text-decoration:underline;text-shadow:0 0 12px rgba(0,245,255,.5)}
.social-btns{display:flex;flex-direction:column;gap:10px;margin-top:4px}
.social-btn{
display:flex;align-items:center;justify-content:center;gap:10px;
width:100%;padding:11px 16px;border-radius:10px;border:1px solid rgba(255,255,255,.1);
background:rgba(255,255,255,.04);color:rgba(255,255,255,.82);
font-size:.88rem;font-weight:600;cursor:pointer;
transition:background .15s,border-color .15s,transform .12s;
text-decoration:none;
}
.social-btn:hover{background:rgba(255,255,255,.09);border-color:rgba(255,255,255,.22);transform:translateY(-1px)}
.social-btn svg{width:18px;height:18px;flex-shrink:0}
</style>
@endpush
@section('content')
<div class="auth-page">
<div class="auth-box">
<div class="auth-logo">
<div class="name">ANIMEXE</div>
<p>Hesabına giriş yap</p>
</div>
@if($errors->any())
<div class="alert alert-err">{{ $errors->first() }}</div>
@endif
<form action="{{ route('frontend.login') }}" method="POST">
@csrf
<div class="f-group">
<label>E-posta</label>
<input type="email" name="email" value="{{ old('email') }}" placeholder="ornek@email.com" required autofocus>
</div>
<div class="f-group">
<label>Şifre</label>
<input type="password" name="password" placeholder="••••••••" required>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:18px">
<div class="f-check" style="margin-bottom:0">
<input type="checkbox" id="rem" name="remember">
<label for="rem">Beni hatırla</label>
</div>
<a href="{{ route('password.request') }}" style="font-size:.82rem;color:rgba(255,255,255,.4);transition:color .15s" onmouseover="this.style.color='var(--accent2)'" onmouseout="this.style.color='rgba(255,255,255,.4)'">Şifremi unuttum</a>
</div>
<button type="submit" class="auth-btn">Giriş Yap</button>
</form>
<div class="auth-sep" style="margin-top:22px">veya şununla devam et</div>
<div class="social-btns">
<a href="{{ route('social.redirect', 'google') }}" class="social-btn">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
Google ile devam et
</a>
<a href="{{ route('social.redirect', 'discord') }}" class="social-btn">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#5865F2"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
Discord ile devam et
</a>
</div>
<div class="auth-switch">
Hesabın yok mu? <a href="{{ route('frontend.register') }}">Üye ol</a>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,110 @@
@extends('frontend.layouts.app')
@section('title','Üye Ol — Animexe')
@push('styles')
<style>
/* ══════════════════════════════════════════════════
REGISTER PAGE PREMIUM (shared with login styles)
═══════════════════════════════════════════════════ */
.auth-page{
min-height:calc(100vh - var(--nav-h));
display:flex;align-items:center;justify-content:center;
padding:40px 16px;position:relative;overflow:hidden;
background:
radial-gradient(ellipse 80% 60% at 50% -20%,rgba(184,77,255,.2),transparent 55%),
radial-gradient(ellipse 50% 40% at 100% 100%,rgba(0,245,255,.1),transparent),
radial-gradient(ellipse 40% 35% at 0% 100%,rgba(255,45,125,.08),transparent);
}
.auth-page::before{
content:'';position:absolute;inset:0;pointer-events:none;
background:linear-gradient(rgba(0,245,255,.03) 1px,transparent 1px),linear-gradient(90deg,rgba(0,245,255,.03) 1px,transparent 1px);
background-size:40px 40px;
mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black,transparent);
}
@keyframes auth-box-in{from{opacity:0;transform:translateY(24px) scale(.97)}to{opacity:1;transform:none}}
.auth-box{
background:linear-gradient(160deg,rgba(14,14,28,.97),rgba(8,8,18,.99));
border:1px solid rgba(0,245,255,.22);border-radius:20px;
padding:clamp(24px,5vw,40px);width:100%;max-width:440px;
box-shadow:0 32px 80px rgba(0,0,0,.65),0 0 60px rgba(0,245,255,.08),0 0 80px rgba(255,45,125,.05);
position:relative;overflow:hidden;
animation:auth-box-in .55s cubic-bezier(.22,1,.36,1) .1s both;
}
.auth-box::before{content:'';position:absolute;top:0;left:10%;right:10%;height:1px;background:linear-gradient(90deg,transparent,var(--accent2),var(--accent),transparent);opacity:.7}
.auth-logo{text-align:center;margin-bottom:24px}
.auth-logo .name{font-family:var(--font-display);font-size:1.4rem;font-weight:900;letter-spacing:.22em;background:linear-gradient(105deg,#fff 0%,var(--accent2) 40%,var(--accent) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;filter:drop-shadow(0 0 20px rgba(0,245,255,.3))}
.auth-logo p{color:rgba(255,255,255,.35);font-size:.85rem;margin-top:5px}
.f-group{margin-bottom:14px;position:relative}
.f-group label{display:block;font-size:.79rem;font-weight:700;color:rgba(255,255,255,.4);margin-bottom:6px;letter-spacing:.05em;text-transform:uppercase}
.f-group input{width:100%;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:12px 16px;color:var(--text);font-size:.9rem;outline:none;transition:border .18s,box-shadow .18s,background .18s}
.f-group input:focus{border-color:var(--accent2);box-shadow:0 0 0 2px rgba(0,245,255,.12),0 0 24px rgba(0,245,255,.06);background:rgba(0,245,255,.04)}
.f-group input::placeholder{color:rgba(255,255,255,.2)}
.f-err{font-size:.78rem;color:#fca5a5;margin-top:5px}
.auth-btn{width:100%;padding:13px;border-radius:12px;border:none;cursor:pointer;background:linear-gradient(135deg,var(--accent),var(--accent3));color:#fff;font-size:.93rem;font-weight:800;letter-spacing:.03em;transition:transform .14s,box-shadow .18s;margin-top:4px;box-shadow:0 0 32px rgba(255,45,125,.35),0 4px 20px rgba(0,0,0,.35);position:relative;overflow:hidden}
.auth-btn:hover{transform:translateY(-2px);box-shadow:0 0 44px rgba(255,45,125,.5),0 6px 24px rgba(0,0,0,.35)}
.auth-terms{font-size:.78rem;color:rgba(255,255,255,.25);text-align:center;margin-top:12px}
.auth-terms a{color:rgba(255,255,255,.45)}
.auth-switch{text-align:center;font-size:.85rem;color:rgba(255,255,255,.35);margin-top:16px}
.auth-switch a{color:var(--accent2);font-weight:700}
.auth-switch a:hover{text-decoration:underline;text-shadow:0 0 12px rgba(0,245,255,.5)}
.auth-sep{text-align:center;margin:18px 0;color:rgba(255,255,255,.2);font-size:.8rem;position:relative}
.auth-sep::before,.auth-sep::after{content:'';position:absolute;top:50%;width:38%;height:1px;background:rgba(255,255,255,.08)}
.auth-sep::before{left:0}.auth-sep::after{right:0}
.social-btns{display:flex;flex-direction:column;gap:10px}
.social-btn{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:11px 16px;border-radius:10px;border:1px solid rgba(255,255,255,.1);background:rgba(255,255,255,.04);color:rgba(255,255,255,.82);font-size:.88rem;font-weight:600;cursor:pointer;transition:background .15s,border-color .15s,transform .12s;text-decoration:none}
.social-btn:hover{background:rgba(255,255,255,.09);border-color:rgba(255,255,255,.22);transform:translateY(-1px)}
.social-btn svg{width:18px;height:18px;flex-shrink:0}
</style>
@endpush
@section('content')
<div class="auth-page">
<div class="auth-box">
<div class="auth-logo">
<div class="name">ANIMEXE</div>
<p>Ücretsiz hesap oluştur</p>
</div>
@if($errors->any())
<div class="alert alert-err">{{ $errors->first() }}</div>
@endif
<form action="{{ route('frontend.register') }}" method="POST">
@csrf
<div class="f-group">
<label>Adın</label>
<input type="text" name="name" value="{{ old('name') }}" placeholder="Adın Soyadın" required autofocus>
</div>
<div class="f-group">
<label>E-posta</label>
<input type="email" name="email" value="{{ old('email') }}" placeholder="ornek@email.com" required>
</div>
<div class="f-group">
<label>Şifre</label>
<input type="password" name="password" placeholder="En az 6 karakter" required>
</div>
<div class="f-group">
<label>Şifre Tekrar</label>
<input type="password" name="password_confirmation" placeholder="Şifreni tekrarla" required>
</div>
<button type="submit" class="auth-btn">Üye Ol Ücretsiz</button>
<p class="auth-terms">Üye olarak <a href="#" style="color:var(--accent)">Kullanım Koşulları</a>'nı kabul etmiş olursunuz.</p>
</form>
<div class="auth-sep">veya şununla kayıt ol</div>
<div class="social-btns">
<a href="{{ route('social.redirect', 'google') }}" class="social-btn">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
Google ile kayıt ol
</a>
<a href="{{ route('social.redirect', 'discord') }}" class="social-btn">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#5865F2"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
Discord ile kayıt ol
</a>
</div>
<div class="auth-switch">
Zaten üye misin? <a href="{{ route('frontend.login') }}">Giriş yap</a>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,53 @@
@extends('frontend.layouts.app')
@section('title','Yeni Şifre Belirle — Animexe')
@push('styles')
<style>
.auth-page{min-height:calc(100vh - var(--nav-h));display:flex;align-items:center;justify-content:center;padding:40px 16px;position:relative;overflow:hidden;background:radial-gradient(ellipse 80% 60% at 50% -20%,rgba(184,77,255,.2),transparent 55%),radial-gradient(ellipse 50% 40% at 100% 100%,rgba(0,245,255,.1),transparent)}
.auth-page::before{content:'';position:absolute;inset:0;pointer-events:none;background:linear-gradient(rgba(0,245,255,.03) 1px,transparent 1px),linear-gradient(90deg,rgba(0,245,255,.03) 1px,transparent 1px);background-size:40px 40px;mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black,transparent)}
@keyframes auth-box-in{from{opacity:0;transform:translateY(24px) scale(.97)}to{opacity:1;transform:none}}
.auth-box{background:linear-gradient(160deg,rgba(14,14,28,.97),rgba(8,8,18,.99));border:1px solid rgba(0,245,255,.22);border-radius:20px;padding:clamp(28px,5vw,44px);width:100%;max-width:440px;box-shadow:0 32px 80px rgba(0,0,0,.65),0 0 60px rgba(0,245,255,.08);position:relative;overflow:hidden;animation:auth-box-in .55s cubic-bezier(.22,1,.36,1) .1s both}
.auth-box::before{content:'';position:absolute;top:0;left:10%;right:10%;height:1px;background:linear-gradient(90deg,transparent,var(--accent2),var(--accent),transparent);opacity:.7}
.auth-logo{text-align:center;margin-bottom:28px}
.auth-logo .name{font-family:var(--font-display);font-size:1.4rem;font-weight:900;letter-spacing:.22em;background:linear-gradient(105deg,#fff,var(--accent2),var(--accent));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;filter:drop-shadow(0 0 20px rgba(0,245,255,.3))}
.auth-logo p{color:rgba(255,255,255,.35);font-size:.85rem;margin-top:5px}
.f-group{margin-bottom:16px}
.f-group label{display:block;font-size:.79rem;font-weight:700;color:rgba(255,255,255,.45);margin-bottom:7px;letter-spacing:.05em;text-transform:uppercase}
.f-group input{width:100%;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:12px 16px;color:var(--text);font-size:.9rem;outline:none;transition:border .18s,box-shadow .18s}
.f-group input:focus{border-color:var(--accent2);box-shadow:0 0 0 2px rgba(0,245,255,.12);background:rgba(0,245,255,.04)}
.f-group input::placeholder{color:rgba(255,255,255,.2)}
.auth-btn{width:100%;padding:13px;border-radius:12px;border:none;cursor:pointer;background:linear-gradient(135deg,var(--accent),var(--accent3));color:#fff;font-size:.93rem;font-weight:800;transition:transform .14s,box-shadow .18s;box-shadow:0 0 32px rgba(255,45,125,.35),0 4px 20px rgba(0,0,0,.35)}
.auth-btn:hover{transform:translateY(-2px)}
</style>
@endpush
@section('content')
<div class="auth-page">
<div class="auth-box">
<div class="auth-logo">
<div class="name">ANIMEXE</div>
<p>Yeni şifre belirle</p>
</div>
@if($errors->any())
<div class="alert alert-err">{{ $errors->first() }}</div>
@endif
<form action="{{ route('password.update') }}" method="POST">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<div class="f-group">
<label>E-posta Adresi</label>
<input type="email" name="email" value="{{ old('email', $email) }}" required>
</div>
<div class="f-group">
<label>Yeni Şifre</label>
<input type="password" name="password" placeholder="En az 6 karakter" required>
</div>
<div class="f-group">
<label>Şifre Tekrar</label>
<input type="password" name="password_confirmation" placeholder="Şifreni tekrarla" required>
</div>
<button type="submit" class="auth-btn">Şifremi Güncelle</button>
</form>
</div>
</div>
@endsection
@@ -0,0 +1,48 @@
@extends('frontend.layouts.app')
@section('title','E-posta Doğrulama — Animexe')
@push('styles')
<style>
.auth-page{min-height:calc(100vh - var(--nav-h));display:flex;align-items:center;justify-content:center;padding:40px 16px;background:radial-gradient(ellipse 80% 60% at 50% -20%,rgba(184,77,255,.2),transparent 55%)}
@keyframes auth-box-in{from{opacity:0;transform:translateY(24px) scale(.97)}to{opacity:1;transform:none}}
.auth-box{background:linear-gradient(160deg,rgba(14,14,28,.97),rgba(8,8,18,.99));border:1px solid rgba(0,245,255,.22);border-radius:20px;padding:clamp(28px,5vw,44px);width:100%;max-width:440px;box-shadow:0 32px 80px rgba(0,0,0,.65);position:relative;overflow:hidden;text-align:center;animation:auth-box-in .55s cubic-bezier(.22,1,.36,1) .1s both}
.auth-box::before{content:'';position:absolute;top:0;left:10%;right:10%;height:1px;background:linear-gradient(90deg,transparent,var(--accent2),var(--accent),transparent);opacity:.7}
.ve-icon{font-size:3rem;margin-bottom:16px;filter:drop-shadow(0 0 16px rgba(0,245,255,.4))}
.ve-title{font-size:1.1rem;font-weight:800;color:#fff;margin-bottom:10px}
.ve-text{font-size:.88rem;color:rgba(255,255,255,.4);line-height:1.7;margin-bottom:24px}
.auth-btn{display:inline-block;padding:12px 28px;border-radius:12px;border:none;cursor:pointer;background:linear-gradient(135deg,var(--accent),var(--accent3));color:#fff;font-size:.9rem;font-weight:800;transition:transform .14s;box-shadow:0 0 28px rgba(255,45,125,.3);width:100%}
.auth-btn:hover{transform:translateY(-2px)}
.status-box{background:rgba(0,245,255,.07);border:1px solid rgba(0,245,255,.2);border-radius:10px;padding:12px 16px;font-size:.85rem;color:var(--accent2);margin-bottom:20px}
.logout-link{display:block;margin-top:16px;font-size:.82rem;color:rgba(255,255,255,.3)}
.logout-link a{color:rgba(255,255,255,.4)}
</style>
@endpush
@section('content')
<div class="auth-page">
<div class="auth-box">
<div class="ve-icon">📧</div>
<div class="ve-title">E-postanı Doğrula</div>
<p class="ve-text">
<strong style="color:#fff">{{ auth()->user()->email }}</strong> adresine doğrulama bağlantısı gönderdik.<br>
Gelen kutunu kontrol et. Spam klasörüne de bakmanı öneririz.
</p>
@if(session('status'))
<div class="status-box"><i class="bi bi-check-circle-fill"></i> {{ session('status') }}</div>
@endif
<form method="POST" action="{{ route('verification.send') }}">
@csrf
<button type="submit" class="auth-btn">
<i class="bi bi-envelope-fill"></i> Tekrar Gönder
</button>
</form>
<div class="logout-link">
<form method="POST" action="{{ route('frontend.logout') }}" style="display:inline">
@csrf
<a href="#" onclick="this.closest('form').submit()">Çıkış yap</a>
</form>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,144 @@
@extends('frontend.layouts.app')
@section('title', 'Anime Blog — Rehberler, İncelemeler ve Öneriler | Animexe')
@section('meta_description', 'Animexe anime blogunda izleme rehberleri, anime incelemeleri, tür önerileri ve AI destekli içerikler keşfedin.')
@section('meta_keywords', 'anime blog, anime rehberi, anime inceleme, anime önerisi, animexe blog')
@section('og_type', 'website')
@push('styles')
<style>
.blog-hero{background:linear-gradient(180deg,rgba(12,12,26,.95) 0%,var(--bg) 100%);padding:calc(var(--nav-h) + 32px) 24px 36px;border-bottom:1px solid rgba(0,245,255,.12)}
.blog-hero h1{font-family:var(--font-display);font-size:1.6rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;margin-bottom:8px}
.blog-hero p{color:var(--text2);font-size:.95rem;max-width:520px}
.blog-grid{display:grid;grid-template-columns:1fr 300px;gap:32px;max-width:1200px;margin:0 auto}
.blog-card{
background:linear-gradient(145deg,rgba(17,17,31,.95),rgba(10,10,20,.98));
border:1px solid rgba(0,245,255,.1);border-radius:var(--r2);overflow:hidden;
transition:transform .2s,box-shadow .22s,border-color .2s;
display:flex;flex-direction:column;
}
.blog-card:hover{transform:translateY(-4px);border-color:rgba(255,45,125,.4);box-shadow:0 16px 48px rgba(0,0,0,.5),0 0 32px rgba(255,45,125,.1)}
.blog-thumb{aspect-ratio:16/9;overflow:hidden;background:var(--surface2)}
.blog-thumb img{width:100%;height:100%;object-fit:cover;transition:transform .3s}
.blog-card:hover .blog-thumb img{transform:scale(1.05)}
.blog-thumb-ph{width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:var(--text3);font-size:2.5rem}
.blog-body{padding:18px;flex:1;display:flex;flex-direction:column}
.blog-category{font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--accent2);margin-bottom:8px}
.blog-title{font-size:.95rem;font-weight:700;color:var(--text);line-height:1.45;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-bottom:8px}
.blog-excerpt{font-size:.8rem;color:var(--text2);line-height:1.55;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;flex:1}
.blog-footer{display:flex;align-items:center;justify-content:space-between;margin-top:14px;padding-top:12px;border-top:1px solid rgba(0,245,255,.08)}
.blog-date{font-size:.72rem;color:var(--text3)}
.blog-read{font-size:.72rem;color:var(--text3);display:flex;align-items:center;gap:4px}
.blog-ai-badge{display:inline-flex;align-items:center;gap:4px;font-size:.65rem;font-weight:700;
padding:2px 8px;border-radius:999px;background:rgba(124,58,237,.2);border:1px solid rgba(124,58,237,.3);
color:#a78bfa;margin-bottom:8px}
.sidebar-card{background:rgba(17,17,31,.8);border:1px solid rgba(0,245,255,.1);border-radius:var(--r2);padding:18px;margin-bottom:20px}
.sidebar-title{font-family:var(--font-display);font-size:.72rem;text-transform:uppercase;letter-spacing:.1em;color:var(--accent2);margin-bottom:14px;font-weight:700}
.sidebar-item{display:flex;gap:10px;padding:8px 0;border-bottom:1px solid rgba(0,245,255,.05);align-items:flex-start}
.sidebar-item:last-child{border-bottom:none}
.sidebar-item-title{font-size:.8rem;font-weight:600;color:var(--text);line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.sidebar-item-date{font-size:.68rem;color:var(--text3);margin-top:3px}
.sidebar-thumb{width:52px;height:52px;border-radius:6px;overflow:hidden;flex-shrink:0;background:var(--surface2)}
.sidebar-thumb img{width:100%;height:100%;object-fit:cover}
.posts-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:20px}
@media(max-width:900px){
.blog-grid{grid-template-columns:1fr}
.blog-sidebar{display:none}
}
@media(max-width:480px){
.posts-grid{grid-template-columns:1fr}
}
</style>
@endpush
@section('content')
<div class="blog-hero">
<div style="max-width:1200px;margin:0 auto">
<h1><i class="bi bi-journal-richtext" style="color:var(--accent2);font-size:1.2rem;margin-right:10px"></i>Anime Blog</h1>
<p>AI destekli anime rehberleri, izleme önerileri ve derinlemesine incelemeler.</p>
</div>
</div>
<div class="section">
<div class="blog-grid">
<div>
@if($posts->count())
<div class="posts-grid">
@foreach($posts as $post)
<a href="{{ route('blog.show', $post->slug) }}" class="blog-card" style="text-decoration:none">
<div class="blog-thumb">
@if($post->coverUrl)
<img src="{{ $post->coverUrl }}" alt="{{ $post->title }}" loading="lazy">
@else
<div class="blog-thumb-ph"><i class="bi bi-journal-richtext"></i></div>
@endif
</div>
<div class="blog-body">
@if($post->ai_generated)
<div class="blog-ai-badge"><i class="bi bi-stars" style="font-size:.6rem"></i> AI İçerik</div>
@endif
@if($post->anime)
<div class="blog-category">{{ $post->anime->title }}</div>
@endif
<div class="blog-title">{{ $post->title }}</div>
<div class="blog-excerpt">{{ $post->excerpt }}</div>
<div class="blog-footer">
<span class="blog-date">{{ $post->published_at?->format('d.m.Y') }}</span>
<span class="blog-read"><i class="bi bi-clock" style="font-size:.65rem"></i> {{ $post->reading_time }} dk</span>
</div>
</div>
</a>
@endforeach
</div>
@if($posts->hasPages())
<div style="margin-top:32px">{{ $posts->links('frontend.pagination') }}</div>
@endif
@else
<div style="text-align:center;padding:80px 20px;color:var(--text3)">
<i class="bi bi-journal-text" style="font-size:3rem;opacity:.25;display:block;margin-bottom:14px"></i>
<p>Henüz blog yazısı yok. Yakında yapay zeka destekli içerikler gelecek!</p>
</div>
@endif
</div>
<aside class="blog-sidebar">
{{-- Son Yazılar --}}
@if($recent->count())
<div class="sidebar-card">
<div class="sidebar-title">Son Yazılar</div>
@foreach($recent as $p)
<a href="{{ route('blog.show', $p->slug) }}" class="sidebar-item" style="text-decoration:none">
<div class="sidebar-thumb">
@if($p->coverUrl)<img src="{{ $p->coverUrl }}" alt="" loading="lazy">
@else<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:var(--text3)"><i class="bi bi-journal-text"></i></div>@endif
</div>
<div>
<div class="sidebar-item-title">{{ $p->title }}</div>
<div class="sidebar-item-date">{{ $p->published_at?->format('d.m.Y') }}</div>
</div>
</a>
@endforeach
</div>
@endif
{{-- En Çok Okunanlar --}}
@if($popular->count())
<div class="sidebar-card">
<div class="sidebar-title"><i class="bi bi-fire" style="color:var(--accent)"></i> En Çok Okunanlar</div>
@foreach($popular as $p)
<a href="{{ route('blog.show', $p->slug) }}" class="sidebar-item" style="text-decoration:none">
<div class="sidebar-thumb">
@if($p->coverUrl)<img src="{{ $p->coverUrl }}" alt="" loading="lazy">
@else<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:var(--text3)"><i class="bi bi-journal-text"></i></div>@endif
</div>
<div>
<div class="sidebar-item-title">{{ $p->title }}</div>
<div class="sidebar-item-date">{{ number_format($p->views) }} görüntülenme</div>
</div>
</a>
@endforeach
</div>
@endif
</aside>
</div>
</div>
@endsection
@@ -0,0 +1,234 @@
@extends('frontend.layouts.app')
@php
$__canon = rtrim(\App\Models\Setting::get('seo_canonical_domain', config('app.url')), '/') . route('blog.show', $post->slug, false);
$__ogImg = $post->coverUrl;
$__pubDate = $post->published_at?->toIso8601String();
$__modDate = $post->updated_at?->toIso8601String();
$__faqItems = $post->faq ?? [];
@endphp
@section('title', ($post->meta_title ?: $post->title) . ' — Animexe Blog')
@section('meta_description', $post->meta_description ?: $post->excerpt)
@section('meta_keywords', $post->meta_keywords)
@section('og_type', 'article')
@section('og_title', $post->title)
@section('og_description', $post->excerpt)
@if($__ogImg)@section('og_image', $__ogImg)@endif
@section('canonical', $__canon)
@section('robots', 'index, follow')
@section('schema')
<script type="application/ld+json">
{
"@@context":"https://schema.org",
"@@type":"BlogPosting",
"headline":"{{ addslashes($post->title) }}",
"description":"{{ addslashes($post->excerpt ?? '') }}",
"url":"{{ $__canon }}",
"datePublished":"{{ $__pubDate }}",
"dateModified":"{{ $__modDate }}",
"author":{"@@type":"Organization","name":"Animexe","url":"{{ rtrim(\App\Models\Setting::get('seo_canonical_domain',config('app.url')),'/') }}"},
"publisher":{"@@type":"Organization","name":"Animexe","logo":{"@@type":"ImageObject","url":"{{ url('/logo.jpg') }}"}},
"image":"{{ $__ogImg ?: url('/logo.jpg') }}",
"inLanguage":"tr-TR",
"keywords":"{{ addslashes($post->meta_keywords ?? '') }}"
}
</script>
@if(count($__faqItems) > 0)
<script type="application/ld+json">
{
"@@context":"https://schema.org",
"@@type":"FAQPage",
"mainEntity":[
@foreach($__faqItems as $i => $faq)
{"@@type":"Question","name":"{{ addslashes($faq['q'] ?? '') }}","acceptedAnswer":{"@@type":"Answer","text":"{{ addslashes($faq['a'] ?? '') }}"}}{{ $i < count($__faqItems)-1 ? ',' : '' }}
@endforeach
]
}
</script>
@endif
<script type="application/ld+json">
{
"@@context":"https://schema.org",
"@@type":"BreadcrumbList",
"itemListElement":[
{"@@type":"ListItem","position":1,"name":"Ana Sayfa","item":"{{ rtrim(\App\Models\Setting::get('seo_canonical_domain',config('app.url')),'/') }}/"},
{"@@type":"ListItem","position":2,"name":"Blog","item":"{{ rtrim(\App\Models\Setting::get('seo_canonical_domain',config('app.url')),'/') }}/blog"},
{"@@type":"ListItem","position":3,"name":"{{ addslashes($post->title) }}","item":"{{ $__canon }}"}
]
}
</script>
@endsection
@push('styles')
<style>
.blog-layout{display:grid;grid-template-columns:1fr 300px;gap:40px;max-width:1160px;margin:0 auto}
.blog-article{}
.blog-hero-img{width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:var(--r2);margin-bottom:28px;
box-shadow:0 8px 32px rgba(0,0,0,.5)}
.blog-meta-bar{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid rgba(0,245,255,.1)}
.blog-meta-item{display:flex;align-items:center;gap:6px;font-size:.78rem;color:var(--text3)}
.blog-meta-item i{font-size:.75rem;color:var(--accent2)}
.blog-ai-badge-lg{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:999px;
background:linear-gradient(135deg,rgba(124,58,237,.2),rgba(232,67,147,.1));
border:1px solid rgba(124,58,237,.3);color:#a78bfa;font-size:.78rem;font-weight:700;margin-bottom:16px}
.blog-h1{font-family:var(--font-display);font-size:1.55rem;font-weight:800;letter-spacing:.04em;
line-height:1.35;color:var(--text);margin-bottom:20px}
.blog-content{color:var(--text2);line-height:1.85;font-size:.95rem}
.blog-content h2{font-family:var(--font-display);font-size:1.1rem;font-weight:700;color:var(--text);
margin:32px 0 14px;letter-spacing:.04em;text-transform:uppercase;
border-left:3px solid var(--accent2);padding-left:12px}
.blog-content h3{font-size:.95rem;font-weight:700;color:var(--text);margin:22px 0 10px}
.blog-content p{margin-bottom:16px}
.blog-content ul,.blog-content ol{padding-left:24px;margin-bottom:16px}
.blog-content li{margin-bottom:8px}
.blog-content strong{color:var(--text);font-weight:700}
.blog-content a{color:var(--accent2);text-decoration:underline;text-decoration-color:rgba(0,245,255,.4)}
.blog-content a:hover{color:var(--accent);text-decoration-color:rgba(255,45,125,.6)}
.faq-section{margin-top:40px;padding-top:28px;border-top:1px solid rgba(0,245,255,.1)}
.faq-section h2{font-family:var(--font-display);font-size:1rem;font-weight:800;text-transform:uppercase;
letter-spacing:.06em;color:var(--accent2);margin-bottom:20px}
.faq-item{background:rgba(17,17,31,.8);border:1px solid rgba(0,245,255,.1);border-radius:var(--r2);
padding:18px;margin-bottom:12px}
.faq-item h3{font-size:.9rem;font-weight:700;color:var(--text);margin-bottom:10px;line-height:1.4}
.faq-item p{font-size:.85rem;color:var(--text2);line-height:1.65;margin:0}
.cta-box{margin-top:36px;padding:28px;background:linear-gradient(135deg,rgba(255,45,125,.12),rgba(0,245,255,.08));
border:1px solid rgba(255,45,125,.3);border-radius:var(--r2);text-align:center}
.cta-box h3{font-family:var(--font-display);font-size:1rem;font-weight:800;text-transform:uppercase;
letter-spacing:.06em;color:var(--text);margin-bottom:12px}
.cta-btn{display:inline-flex;align-items:center;gap:8px;padding:12px 28px;border-radius:999px;
background:linear-gradient(135deg,var(--accent),var(--accent3));color:#fff;
font-weight:700;font-size:.9rem;text-decoration:none;transition:transform .16s,box-shadow .18s;
box-shadow:0 0 24px rgba(255,45,125,.4)}
.cta-btn:hover{transform:translateY(-2px);box-shadow:0 0 36px rgba(255,45,125,.55);color:#fff}
.related-card{
background:rgba(17,17,31,.8);border:1px solid rgba(0,245,255,.1);border-radius:var(--r);
overflow:hidden;transition:border-color .2s;display:flex;gap:12px;align-items:flex-start;padding:12px;
}
.related-card:hover{border-color:rgba(255,45,125,.3)}
.related-thumb{width:64px;height:64px;border-radius:6px;overflow:hidden;flex-shrink:0;background:var(--surface2)}
.related-thumb img{width:100%;height:100%;object-fit:cover}
.linked-animes{margin-top:28px;padding:20px;background:rgba(0,245,255,.04);border:1px solid rgba(0,245,255,.12);border-radius:var(--r2)}
.linked-animes h3{font-size:.82rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--accent2);margin-bottom:14px}
.la-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px}
@media(max-width:900px){
.blog-layout{grid-template-columns:1fr}
.blog-sidebar-wrap{display:none}
}
</style>
@endpush
@section('content')
<div class="section" style="padding-top:calc(var(--nav-h) + 28px)">
<div class="blog-layout">
<article class="blog-article" itemscope itemtype="https://schema.org/BlogPosting">
@if($post->coverUrl)
<img src="{{ $post->coverUrl }}" alt="{{ $post->title }}"
class="blog-hero-img" itemprop="image" fetchpriority="high">
@endif
@if($post->ai_generated)
<div class="blog-ai-badge-lg"><i class="bi bi-stars"></i> AI Destekli İçerik</div>
@endif
<h1 class="blog-h1" itemprop="headline">{{ $post->title }}</h1>
<div class="blog-meta-bar">
@if($post->published_at)
<span class="blog-meta-item">
<i class="bi bi-calendar3"></i>
<time itemprop="datePublished" datetime="{{ $post->published_at->toIso8601String() }}">
{{ $post->published_at->format('d F Y') }}
</time>
</span>
@endif
<span class="blog-meta-item"><i class="bi bi-clock"></i> {{ $post->reading_time }} dk okuma</span>
@if($post->anime)
<span class="blog-meta-item">
<i class="bi bi-play-circle"></i>
<a href="{{ $post->anime?->detailUrl ?? '#' }}" style="color:var(--accent2)">{{ $post->anime->title }}</a>
</span>
@endif
@if($post->focus_keyword)
<span class="blog-meta-item"><i class="bi bi-tag"></i> {{ $post->focus_keyword }}</span>
@endif
</div>
<div class="blog-content" itemprop="articleBody">
{!! $post->content !!}
</div>
{{-- FAQ Bölümü --}}
@if(count($__faqItems) > 0)
<div class="faq-section">
<h2>Sıkça Sorulan Sorular</h2>
@foreach($__faqItems as $faq)
<div class="faq-item">
<h3>{{ $faq['q'] ?? '' }}</h3>
<p>{{ $faq['a'] ?? '' }}</p>
</div>
@endforeach
</div>
@endif
{{-- İlgili Anime Linkleri --}}
@if($linkedAnimes->count())
<div class="linked-animes">
<h3><i class="bi bi-collection-play me-1"></i>Bu Yazıda Bahsedilen Animeler</h3>
<div class="la-grid">
@foreach($linkedAnimes as $la)
<a href="{{ $la->detailUrl }}" class="a-card">
<div class="a-poster">
@if($la->coverUrl)<img src="{{ $la->coverUrl }}" alt="{{ $la->title }}" loading="lazy">
@else<div class="a-no-img"><i class="bi bi-image"></i></div>@endif
@if($la->rating)<span class="a-rating"><i class="bi bi-star-fill" style="font-size:.6rem"></i>{{ number_format($la->rating,1) }}</span>@endif
</div>
<div class="a-info">
<div class="a-title">{{ $la->title }}</div>
<div class="a-meta">{{ $la->release_year }}</div>
</div>
</a>
@endforeach
</div>
</div>
@endif
{{-- CTA --}}
@if($post->anime)
<div class="cta-box">
<h3>{{ $post->anime->title }} İzle</h3>
<p style="color:var(--text2);font-size:.88rem;margin-bottom:18px">
Animexe'de Türkçe altyazılı veya dublajlı ücretsiz izle.
</p>
<a href="{{ $post->anime?->detailUrl ?? '#' }}" class="cta-btn">
<i class="bi bi-play-fill"></i> Hemen İzle
</a>
</div>
@endif
</article>
{{-- Sidebar --}}
<aside class="blog-sidebar-wrap">
@if($related->count())
<div style="background:rgba(17,17,31,.8);border:1px solid rgba(0,245,255,.1);border-radius:var(--r2);padding:18px">
<div style="font-family:var(--font-display);font-size:.72rem;text-transform:uppercase;letter-spacing:.1em;color:var(--accent2);margin-bottom:14px;font-weight:700">İlgili Yazılar</div>
<div style="display:flex;flex-direction:column;gap:10px">
@foreach($related as $rp)
<a href="{{ route('blog.show', $rp->slug) }}" class="related-card" style="text-decoration:none">
<div class="related-thumb">
@if($rp->coverUrl)<img src="{{ $rp->coverUrl }}" alt="" loading="lazy">
@else<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:var(--text3)"><i class="bi bi-journal-text"></i></div>@endif
</div>
<div>
<div style="font-size:.8rem;font-weight:600;color:var(--text);line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden">{{ $rp->title }}</div>
<div style="font-size:.68rem;color:var(--text3);margin-top:4px">{{ $rp->published_at?->format('d.m.Y') }}</div>
</div>
</a>
@endforeach
</div>
</div>
@endif
</aside>
</div>
</div>
@endsection
+114
View File
@@ -0,0 +1,114 @@
@extends('frontend.layouts.app')
@section('title', 'Zaman Kapsüllerim | Animexe')
@push('styles')
<style>
.cap-hero{padding-top:calc(var(--nav-h) + 40px);padding-bottom:32px;text-align:center;background:radial-gradient(ellipse at 50% 0%,rgba(100,80,255,.12) 0%,transparent 70%)}
.cap-hero h1{font-size:2rem;font-weight:900;margin-bottom:8px}
.cap-hero h1 span{color:#a78bfa}
.cap-hero p{color:rgba(255,255,255,.4);font-size:.9rem}
.cap-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px;padding:0 0 60px}
.cap-card{border-radius:16px;padding:20px;position:relative;overflow:hidden;border:1px solid rgba(255,255,255,.08)}
.cap-card.locked{background:linear-gradient(135deg,rgba(20,18,40,.9),rgba(10,8,22,.9))}
.cap-card.unlocked{background:linear-gradient(135deg,rgba(100,80,255,.12),rgba(60,40,180,.08));border-color:rgba(167,139,250,.25)}
.cap-card.opened{background:linear-gradient(135deg,rgba(0,200,100,.06),rgba(0,160,80,.04));border-color:rgba(0,200,100,.2)}
.cap-lock-icon{font-size:2rem;margin-bottom:10px;display:block}
.cap-anime{font-size:.72rem;font-weight:700;color:rgba(167,139,250,.8);text-transform:uppercase;letter-spacing:.07em;margin-bottom:6px}
.cap-date{font-size:.78rem;color:rgba(255,255,255,.35);margin-bottom:14px}
.cap-locked-msg{font-size:.82rem;color:rgba(255,255,255,.25);text-align:center;padding:16px 0}
.cap-message{font-size:.88rem;line-height:1.6;color:rgba(255,255,255,.8);background:rgba(255,255,255,.04);border-radius:10px;padding:14px;border-left:3px solid #a78bfa;font-style:italic}
.cap-open-btn{width:100%;margin-top:14px;padding:10px;border-radius:10px;border:none;background:rgba(167,139,250,.2);color:#a78bfa;font-weight:700;font-size:.88rem;cursor:pointer;font-family:inherit;transition:background .15s}
.cap-open-btn:hover{background:rgba(167,139,250,.35)}
.cap-tag{display:inline-block;padding:2px 10px;border-radius:20px;font-size:.65rem;font-weight:700;margin-bottom:10px}
.cap-tag.locked{background:rgba(255,255,255,.06);color:rgba(255,255,255,.3)}
.cap-tag.unlocked{background:rgba(167,139,250,.12);color:#a78bfa}
.cap-tag.opened{background:rgba(0,200,100,.1);color:#00c864}
.cap-countdown{font-size:.78rem;color:rgba(255,255,255,.3);margin-top:8px;text-align:center}
</style>
@endpush
@section('content')
<div style="max-width:1100px;margin:0 auto;padding:0 20px">
<div class="cap-hero">
<h1> Zaman <span>Kapsüllerim</span></h1>
<p>Geçmiş benliğinden gelen mesajlar.</p>
</div>
@if($capsules->isEmpty())
<div style="text-align:center;padding:80px 20px;color:rgba(255,255,255,.25)">
<i class="bi bi-hourglass" style="font-size:3rem;display:block;margin-bottom:16px"></i>
Henüz kapsül oluşturmadın.<br>
<small style="font-size:.8rem">Bir anime sayfasından ilk kez izlerken kapsül bırakabilirsin.</small>
</div>
@else
<div class="cap-grid">
@foreach($capsules as $cap)
<div class="cap-card {{ $cap['opened'] ? 'opened' : ($cap['unlocked'] ? 'unlocked' : 'locked') }}" id="cap-{{ $cap['id'] }}">
<span class="cap-lock-icon">{{ $cap['opened'] ? '📭' : ($cap['unlocked'] ? '🔓' : '🔒') }}</span>
<span class="cap-tag {{ $cap['opened'] ? 'opened' : ($cap['unlocked'] ? 'unlocked' : 'locked') }}">
{{ $cap['opened'] ? 'Açıldı' : ($cap['unlocked'] ? 'Açılabilir' : 'Kilitli') }}
</span>
<div class="cap-anime">
<a href="{{ $cap['anime_url'] }}" style="color:inherit;text-decoration:none">{{ $cap['anime'] }}</a>
</div>
<div class="cap-date">
<i class="bi bi-calendar3"></i>
Oluşturuldu: {{ $cap['created_at'] }} · Açılış: {{ $cap['unlock_at'] }}
</div>
@if($cap['opened'] || $cap['unlocked'])
@if($cap['message'])
<div class="cap-message">{{ $cap['message'] }}</div>
@else
<button class="cap-open-btn" onclick="openCapsule({{ $cap['id'] }}, this)">
<i class="bi bi-envelope-open"></i> Kapsülü
</button>
@endif
@else
<div class="cap-locked-msg">
<i class="bi bi-lock"></i><br>
Bu kapsül {{ $cap['unlock_at'] }} tarihinde açılacak
</div>
@endif
</div>
@endforeach
</div>
@endif
</div>
@endsection
@push('scripts')
<script>
const CSRF = document.querySelector('meta[name="csrf-token"]')?.content ?? '';
async function openCapsule(id, btn) {
btn.disabled = true;
btn.textContent = 'Açılıyor...';
const r = await fetch(`/kapsul/${id}/ac`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json' },
body: JSON.stringify({}),
});
const d = await r.json();
if (d.ok) {
const card = document.getElementById('cap-' + id);
btn.remove();
const msg = document.createElement('div');
msg.className = 'cap-message';
msg.textContent = d.message;
card.appendChild(msg);
card.classList.remove('unlocked');
card.classList.add('opened');
card.querySelector('.cap-lock-icon').textContent = '📭';
const tag = card.querySelector('.cap-tag');
tag.textContent = 'Açıldı';
tag.className = 'cap-tag opened';
} else {
btn.disabled = false;
btn.textContent = 'Kapsülü Aç';
if (window.showToast) showToast(d.error || 'Hata oluştu.', 'error');
else alert(d.error || 'Hata oluştu.');
}
}
</script>
@endpush
@@ -0,0 +1,35 @@
@extends('frontend.layouts.app')
@section('title', 'Ödeme Başarısız | Animexe')
@push('styles')
<style>
.ck-result{max-width:480px;margin:80px auto;text-align:center;padding:0 16px}
.ck-result-icon{font-size:4rem;margin-bottom:20px;color:#ff4d6d}
.ck-result h1{font-size:1.5rem;font-weight:800;margin-bottom:10px}
.ck-result p{color:rgba(255,255,255,.55);margin-bottom:28px;line-height:1.6}
.ck-result-btns{display:flex;gap:12px;justify-content:center;flex-wrap:wrap}
.ck-result-btn{display:inline-flex;align-items:center;gap:8px;padding:12px 24px;border-radius:10px;font-weight:700;text-decoration:none;transition:opacity .15s}
.ck-result-btn.primary{background:linear-gradient(135deg,#00f5ff,#b84dff);color:#fff}
.ck-result-btn.secondary{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);color:rgba(255,255,255,.7)}
.ck-result-btn:hover{opacity:.85;color:inherit}
</style>
@endpush
@section('content')
<div class="ck-result">
<div class="ck-result-icon"><i class="bi bi-x-circle-fill"></i></div>
<h1>Ödeme Başarısız</h1>
<p>
Ödeme işlemi tamamlanamadı. Kart bilgilerini kontrol edip tekrar deneyebilirsin
veya farklı bir kart kullanabilirsin.
</p>
<div class="ck-result-btns">
<a href="{{ route('premium.plans') }}" class="ck-result-btn primary">
<i class="bi bi-arrow-repeat"></i> Tekrar Dene
</a>
<a href="{{ route('anime.index') }}" class="ck-result-btn secondary">
<i class="bi bi-house"></i> Ana Sayfa
</a>
</div>
</div>
@endsection
@@ -0,0 +1,31 @@
@extends('frontend.layouts.app')
@section('title', 'Ödeme | Animexe')
@push('styles')
<style>
.ck-pay-wrap{max-width:540px;margin:40px auto;padding:0 16px}
.ck-pay-head{text-align:center;margin-bottom:24px}
.ck-pay-head h1{font-size:1.2rem;font-weight:700;margin-bottom:6px}
.ck-pay-head p{color:rgba(255,255,255,.45);font-size:.85rem}
.ck-iyzico-box{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.08);border-radius:16px;padding:24px;min-height:400px}
.ck-secure{display:flex;align-items:center;gap:8px;justify-content:center;margin-top:16px;font-size:.75rem;color:rgba(255,255,255,.3)}
</style>
@endpush
@section('content')
<div class="ck-pay-wrap">
<div class="ck-pay-head">
<h1><i class="bi bi-credit-card-fill me-2" style="color:#00f5ff"></i>Kart Bilgileri</h1>
<p>{{ $plan->name }} {{ number_format($plan->price, 0) }} / {{ $plan->duration_days }} gün</p>
</div>
<div class="ck-iyzico-box">
{!! $formContent !!}
</div>
<div class="ck-secure">
<i class="bi bi-shield-check" style="color:#00f5ff"></i>
Kart bilgileriniz iyzico'nun güvenli sunucularında işlenir
</div>
</div>
@endsection
@@ -0,0 +1,104 @@
@extends('frontend.layouts.app')
@section('title', $plan->name . ' — Ödeme | Animexe')
@push('styles')
<style>
.ck-wrap{max-width:860px;margin:40px auto;padding:0 16px;display:grid;grid-template-columns:1fr 1fr;gap:28px}
@media(max-width:640px){.ck-wrap{grid-template-columns:1fr}}
.ck-plan{background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:16px;padding:28px}
.ck-plan-name{font-size:1.4rem;font-weight:800;color:#00f5ff;margin-bottom:6px}
.ck-plan-price{font-size:2.4rem;font-weight:900;margin:12px 0 4px}
.ck-plan-dur{color:rgba(255,255,255,.45);font-size:.85rem;margin-bottom:20px}
.ck-features{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:8px}
.ck-features li{color:rgba(255,255,255,.7);font-size:.88rem;display:flex;align-items:center;gap:8px}
.ck-features li i{color:#00f5ff;font-size:.9rem}
.ck-form{background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:16px;padding:28px}
.ck-form h2{font-size:1rem;font-weight:700;margin-bottom:20px;color:rgba(255,255,255,.85)}
.ck-field{margin-bottom:16px}
.ck-field label{display:block;font-size:.78rem;color:rgba(255,255,255,.5);margin-bottom:5px;font-weight:600;text-transform:uppercase;letter-spacing:.04em}
.ck-field input{width:100%;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);border-radius:8px;padding:10px 14px;color:#fff;font-size:.9rem;outline:none;transition:border-color .15s;box-sizing:border-box}
.ck-field input:focus{border-color:rgba(0,245,255,.4)}
.ck-field input::placeholder{color:rgba(255,255,255,.25)}
.ck-field small{display:block;font-size:.73rem;color:rgba(255,255,255,.3);margin-top:4px}
.ck-submit{width:100%;padding:13px;background:linear-gradient(135deg,#00f5ff,#b84dff);border:none;border-radius:10px;color:#fff;font-weight:800;font-size:1rem;cursor:pointer;margin-top:6px;transition:opacity .15s}
.ck-submit:hover{opacity:.9}
.ck-secure{display:flex;align-items:center;gap:8px;justify-content:center;margin-top:14px;font-size:.75rem;color:rgba(255,255,255,.3)}
.ck-back{display:inline-flex;align-items:center;gap:6px;color:rgba(255,255,255,.4);font-size:.82rem;margin-bottom:24px;text-decoration:none}
.ck-back:hover{color:rgba(255,255,255,.7)}
.ck-err{background:rgba(255,60,60,.12);border:1px solid rgba(255,60,60,.3);border-radius:8px;padding:10px 14px;color:#ff6b6b;font-size:.85rem;margin-bottom:16px}
</style>
@endpush
@section('content')
<div class="ck-wrap">
<div>
<a href="{{ route('premium.plans') }}" class="ck-back"><i class="bi bi-arrow-left"></i> Planlara dön</a>
<div class="ck-plan">
<div class="ck-plan-name">{{ $plan->name }}</div>
<div class="ck-plan-price"><span style="font-size:1.2rem;font-weight:400"></span>{{ number_format($plan->price, 0) }}</div>
<div class="ck-plan-dur">{{ $plan->duration_days }} günlük premium üyelik</div>
@if(($plan->trial_days ?? 0) > 0)
<div style="display:inline-flex;align-items:center;gap:6px;padding:5px 12px;border-radius:20px;background:rgba(0,245,255,.1);border:1px solid rgba(0,245,255,.25);color:#00f5ff;font-size:.75rem;font-weight:700;margin-bottom:16px">
<i class="bi bi-gift-fill"></i> İlk {{ $plan->trial_days }} gün ücretsiz
</div>
@endif
@if(!empty($plan->features))
<ul class="ck-features">
@foreach($plan->features as $feat)
<li><i class="bi bi-check2-circle"></i> {{ $feat }}</li>
@endforeach
</ul>
@endif
</div>
</div>
<div class="ck-form">
<h2><i class="bi bi-person-fill me-2"></i>Fatura Bilgileri</h2>
@if($errors->has('general'))
<div class="ck-err"><i class="bi bi-exclamation-circle me-1"></i>{{ $errors->first('general') }}</div>
@endif
<form method="POST" action="{{ route('checkout.initialize', $plan) }}">
@csrf
<div class="ck-field">
<label>Ad Soyad *</label>
<input type="text" name="full_name" value="{{ old('full_name', auth()->user()->name) }}" placeholder="Adınız Soyadınız" required>
@error('full_name')<span style="color:#ff6b6b;font-size:.75rem">{{ $message }}</span>@enderror
</div>
<div class="ck-field">
<label>E-posta</label>
<input type="text" value="{{ auth()->user()->email }}" disabled style="opacity:.5;cursor:not-allowed">
</div>
<div class="ck-field">
<label>Telefon *</label>
<input type="tel" name="phone" value="{{ old('phone') }}" placeholder="05XX XXX XX XX" required>
@error('phone')<span style="color:#ff6b6b;font-size:.75rem">{{ $message }}</span>@enderror
</div>
<div class="ck-field">
<label>İl *</label>
<input type="text" name="city" value="{{ old('city') }}" placeholder="İstanbul" required>
@error('city')<span style="color:#ff6b6b;font-size:.75rem">{{ $message }}</span>@enderror
</div>
<div class="ck-field">
<label>Adres *</label>
<input type="text" name="address" value="{{ old('address') }}" placeholder="Mahalle, sokak, bina no..." required>
@error('address')<span style="color:#ff6b6b;font-size:.75rem">{{ $message }}</span>@enderror
</div>
<div class="ck-field">
<label>TC Kimlik No <span style="color:rgba(255,255,255,.3)">(isteğe bağlı)</span></label>
<input type="text" name="identity_no" value="{{ old('identity_no') }}" placeholder="11 haneli TC no" maxlength="11" inputmode="numeric">
<small>iyzico güvenlik doğrulaması için istenir</small>
@error('identity_no')<span style="color:#ff6b6b;font-size:.75rem">{{ $message }}</span>@enderror
</div>
<button type="submit" class="ck-submit">
<i class="bi bi-credit-card-fill me-2"></i>Ödemeye Geç {{ number_format($plan->price, 0) }}
</button>
</form>
<div class="ck-secure">
<i class="bi bi-shield-check" style="color:#00f5ff"></i>
iyzico altyapısıyla güvenli ödeme
</div>
</div>
</div>
@endsection
@@ -0,0 +1,29 @@
@extends('frontend.layouts.app')
@section('title', 'Ödeme Başarılı | Animexe')
@push('styles')
<style>
.ck-result{max-width:480px;margin:80px auto;text-align:center;padding:0 16px}
.ck-result-icon{font-size:4rem;margin-bottom:20px;color:#00d4a4}
.ck-result h1{font-size:1.5rem;font-weight:800;margin-bottom:10px}
.ck-result p{color:rgba(255,255,255,.55);margin-bottom:28px;line-height:1.6}
.ck-result-btn{display:inline-flex;align-items:center;gap:8px;padding:12px 28px;border-radius:10px;background:linear-gradient(135deg,#00f5ff,#b84dff);color:#fff;font-weight:700;text-decoration:none;transition:opacity .15s}
.ck-result-btn:hover{opacity:.9;color:#fff}
</style>
@endpush
@section('content')
<div class="ck-result">
<div class="ck-result-icon"><i class="bi bi-check-circle-fill"></i></div>
<h1>Premium Aktif! 🎉</h1>
<p>
@if(session('checkout_plan_name'))
<strong>{{ session('checkout_plan_name') }}</strong> planın aktif edildi.<br>
@endif
Artık tüm premium özelliklerden yararlanabilirsin.
</p>
<a href="{{ route('profile.settings') }}" class="ck-result-btn">
<i class="bi bi-stars"></i> Kozmetiklerini Ayarla
</a>
</div>
@endsection
File diff suppressed because it is too large Load Diff
+96
View File
@@ -0,0 +1,96 @@
@extends('frontend.layouts.app')
@php
$__genreCanon = rtrim(\App\Models\Setting::get('seo_canonical_domain', config('app.url')), '/') . route('genre', $genre->slug, false);
$__genreDesc = $genre->name . ' türündeki en iyi anime dizileri ve filmlerini Animexe\'de Türkçe altyazılı veya dublajlı ücretsiz izleyin. ' . $genre->name . ' anime listesi.';
@endphp
@section('title', $genre->name . ' Anime İzle - Türkçe Altyazılı | Animexe')
@section('meta_description', $__genreDesc)
@section('meta_keywords', $genre->name . ' anime izle, ' . $genre->name . ' türkçe anime izle, ' . $genre->name . ' anime, türkçe ' . $genre->name . ' anime, animexe ' . strtolower($genre->name))
@section('og_title', $genre->name . ' Türündeki Animeler — Animexe')
@section('og_description', $__genreDesc)
@section('canonical', $__genreCanon)
@section('schema')
@if((\App\Models\Setting::get('seo_enable_schema','1')) === '1')
@php $__gDomain = rtrim(\App\Models\Setting::get('seo_canonical_domain',config('app.url')),'/'); @endphp
{{-- CollectionPage + ItemList --}}
<script type="application/ld+json">
{
"@@context":"https://schema.org",
"@@type":"CollectionPage",
"name":"{{ addslashes($genre->name) }} Anime İzle - Türkçe Altyazılı",
"description":"{{ addslashes($__genreDesc) }}",
"url":"{{ $__genreCanon }}",
"inLanguage":"tr-TR",
"mainEntity":{
"@@type":"ItemList",
"name":"{{ addslashes($genre->name) }} Anime Listesi",
"numberOfItems":{{ $animes->total() }},
"itemListElement":[
@foreach($animes->take(10) as $i => $ga)
{"@@type":"ListItem","position":{{ $i+1 }},"name":"{{ addslashes($ga->title) }} İzle","url":"{{ $__gDomain }}/anime/{{ $ga->slug }}"}{{ $i < min(9,$animes->count()-1) ? ',' : '' }}
@endforeach
]
}
}
</script>
{{-- BreadcrumbList --}}
@if((\App\Models\Setting::get('seo_enable_breadcrumb','1')) === '1')
<script type="application/ld+json">
{
"@@context":"https://schema.org",
"@@type":"BreadcrumbList",
"itemListElement":[
{"@@type":"ListItem","position":1,"name":"Ana Sayfa","item":"{{ $__gDomain }}/"},
{"@@type":"ListItem","position":2,"name":"Türler","item":"{{ $__gDomain }}/search"},
{"@@type":"ListItem","position":3,"name":"{{ addslashes($genre->name) }} Anime İzle","item":"{{ $__genreCanon }}"}
]
}
</script>
@endif
@endif
@endsection
@section('content')
<div class="section" style="padding-top:36px">
<div class="section-hdr" style="margin-bottom:18px">
<span class="section-title">
<i class="bi bi-tag" style="font-size:.85rem;color:var(--accent2)"></i>
{{ $genre->name }}
</span>
<span style="color:var(--text3);font-size:.82rem">{{ $animes->total() }} anime</span>
</div>
@if($animes->count())
<div class="a-grid">
@foreach($animes as $anime)
<a href="{{ $anime->detailUrl }}" class="a-card">
<div class="a-poster">
@if($anime->coverUrl)
<img src="{{ $anime->coverUrl }}" alt="{{ $anime->title }}" loading="lazy">
@else
<div class="a-no-img"><i class="bi bi-image"></i></div>
@endif
@if($anime->type==='movie')<span class="a-badge">Film</span>@endif
@if($anime->rating)<span class="a-rating"><i class="bi bi-star-fill" style="font-size:.65rem"></i>{{ number_format($anime->rating,1) }}</span>@endif
<div class="a-hover-play"><div class="play-circle"><i class="bi bi-play-fill"></i></div></div>
</div>
<div class="a-info">
<div class="a-title">{{ $anime->title }}</div>
<div class="a-meta">{{ $anime->release_year }}</div>
</div>
</a>
@endforeach
</div>
@if($animes->hasPages())
<div style="margin-top:32px">{{ $animes->links('frontend.pagination') }}</div>
@endif
@else
<div style="text-align:center;padding:64px 20px;color:var(--text3)">
<i class="bi bi-collection" style="font-size:3rem;opacity:.25;display:block;margin-bottom:14px"></i>
<p>Bu türde henüz anime bulunmuyor.</p>
</div>
@endif
</div>
@endsection
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,118 @@
@extends('frontend.layouts.app')
@section('title', 'Telif Hakkı & DMCA | Animexe')
@push('styles')
<style>
.legal-hero{padding-top:calc(var(--nav-h) + 48px);padding-bottom:28px;text-align:center}
.legal-hero h1{font-size:1.8rem;font-weight:900;margin-bottom:8px}
.legal-hero p{color:rgba(255,255,255,.4);font-size:.9rem}
.legal-body{max-width:820px;margin:0 auto;padding:0 20px 80px}
.legal-section{margin-bottom:36px}
.legal-section h2{font-size:1.05rem;font-weight:800;color:#ff2d7d;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,.07)}
.legal-section p,.legal-section li{font-size:.88rem;color:rgba(255,255,255,.65);line-height:1.8;margin-bottom:8px}
.legal-section ul{padding-left:20px}
.legal-section li{margin-bottom:4px}
.legal-warn{background:rgba(245,158,11,.07);border:1px solid rgba(245,158,11,.25);border-radius:12px;padding:16px 20px;margin-bottom:32px}
.legal-warn p{color:rgba(255,230,150,.7);font-size:.83rem;margin:0;line-height:1.7}
.dmca-box{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:24px;margin-bottom:24px}
.dmca-box h3{font-size:.95rem;font-weight:800;margin-bottom:14px;color:rgba(255,255,255,.85)}
.dmca-box ol{padding-left:20px}
.dmca-box li{font-size:.85rem;color:rgba(255,255,255,.6);line-height:1.8;margin-bottom:6px}
.dmca-email{display:inline-block;background:rgba(255,45,125,.12);border:1px solid rgba(255,45,125,.25);border-radius:10px;padding:14px 20px;color:#ff2d7d;font-weight:700;font-size:.9rem;text-decoration:none;margin-top:8px}
.legal-updated{font-size:.78rem;color:rgba(255,255,255,.25);text-align:right;margin-bottom:32px}
</style>
@endpush
@section('content')
<div style="max-width:1200px;margin:0 auto">
<div class="legal-hero">
<h1>Telif Hakkı & DMCA</h1>
<p>İçerik kaldırma talepleri ve telif hakkı politikası</p>
</div>
<div class="legal-body">
<div class="legal-warn">
<p><strong>Önemli:</strong> Animexe herhangi bir video içeriğini sunucularında barındırmaz. Tüm videolar üçüncü taraf platformlara embed edilmektedir. Hak sahiplerinin içerik kaldırma taleplerine en kısa sürede yanıt veririz.</p>
</div>
<div class="legal-section">
<h2>1. Platform Statüsü Teknik Aracı</h2>
<p>Animexe, <strong>5651 sayılı Kanun</strong> kapsamında bir <strong>yer sağlayıcı</strong> ve teknik aracıdır. Platform:</p>
<ul>
<li>Video içeriklerini kendi sunucularında <strong>barındırmaz, depolamaz veya yüklemez</strong>.</li>
<li>Üçüncü taraf video barındırma platformlarından (YouTube, Dailymotion, Drive vb.) iframe/embed yöntemiyle içerik gösterir.</li>
<li>İçeriklerin telif hakkı sahipliği, orijinal barındırma platformlarına aittir.</li>
<li>İçerik kaldırma taleplerini alır ve ilgili üçüncü taraflarla koordineli biçimde işler.</li>
</ul>
<p>Anime içerikleri üzerindeki telif hakları; ilgili Japon yapım şirketlerine, yayıncılarına ve lisans sahiplerine aittir. Animexe bu içerikler üzerinde herhangi bir hak iddia etmez.</p>
</div>
<div class="legal-section">
<h2>2. Telif Hakkı İhlali Bildirimi (DMCA / 5651)</h2>
<p>İçeriğinizin izniniz olmadan Platform'da yayımlandığını düşünüyorsanız aşağıdaki bilgilerle bize ulaşın:</p>
<div class="dmca-box">
<h3>📋 Bildirimde Bulunması Gereken Bilgiler</h3>
<ol>
<li>Hak sahibinin (veya yetkili temsilcinin) <strong>adı, soyadı ve iletişim bilgileri</strong></li>
<li>Telif hakkı ihlali iddia edilen <strong>orijinal eserin tanımı</strong></li>
<li>Kaldırılmasını talep ettiğiniz <strong>Platform üzerindeki içeriğin tam URL'si</strong></li>
<li>Söz konusu kullanımın yasal olarak yetkilendirilmediğine <strong>iyi niyet beyanı</strong></li>
<li>Verdiğiniz bilgilerin doğru olduğuna ve hak sahibi ya da yetkili temsilci sıfatıyla hareket ettiğinize dair <strong>beyan</strong></li>
<li>İmza (elektronik veya ıslak imza)</li>
</ol>
</div>
<p>Bildiriminizi şu adrese gönderin:</p>
<a class="dmca-email" href="mailto:dmca@animexe.com">
dmca@animexe.com
</a>
<br><br>
<p>Geçerli bir bildirim aldıktan sonra ilgili içeriği <strong>en geç 72 saat içinde</strong> kaldırır veya erişimi engelleriz.</p>
</div>
<div class="legal-section">
<h2>3. Karşı Bildirim (Counter-Notice)</h2>
<p>İçeriğinizin hatalı olarak kaldırıldığını düşünüyorsanız karşı bildirimde bulunabilirsiniz. Karşı bildirimde şunlar yer almalıdır:</p>
<ul>
<li>Kimlik bilgileriniz ve iletişim bilgileriniz</li>
<li>Kaldırılan içeriğin açıklaması ve URL'si</li>
<li>İçeriğin hatalı kaldırıldığına dair iyi niyet beyanı</li>
<li>Verilen bilgilerin doğru olduğuna dair yemin beyanı</li>
</ul>
<p>Karşı bildirimler orijinal şikâyet sahibine iletilir ve 10 günü içinde değerlendirilir.</p>
</div>
<div class="legal-section">
<h2>4. Tekrarlayan İhlaller Politikası</h2>
<p>Birden fazla kez telif hakkı ihlali tespit edilen kullanıcıların hesapları <strong>kalıcı olarak kapatılır</strong>. Platform, bu konuda DMCA tekrarlayan ihlalci politikasına uygun hareket eder.</p>
</div>
<div class="legal-section">
<h2>5. Hukuki Koruma ve Adil Kullanım</h2>
<p>Platform, aşağıdaki ilkeler çerçevesinde hareket ettiğini beyan eder:</p>
<ul>
<li>ABD hukukunda DMCA Safe Harbor (17 U.S.C. §512), Türk hukukunda 5651 sayılı Kanun kapsamında teknik aracı korumasından yararlanmaktadır.</li>
<li>Anime tanıtım görselleri, karakter isimleri ve başlıklar "adil kullanım" (fair use) / tanıtım amacıyla kullanılmakta olup ticari amaç taşımamaktadır.</li>
<li>Platform, içeriklerden herhangi bir reklam geliri elde etmemektedir (içerik barındırılmadığından).</li>
</ul>
</div>
<div class="legal-section">
<h2>6. Hukuki Süreç Talepleri</h2>
<p>Mahkeme kararı, savcılık müzekkeresi veya resmi hukuki talepleri aşağıdaki adrese iletiniz:</p>
<p><a href="mailto:hukuk@animexe.com" style="color:#ff2d7d">hukuk@animexe.com</a></p>
<p>Talepler, Türkiye Cumhuriyeti mahkemelerinin yetkisi dahilinde değerlendirilir.</p>
</div>
<div class="legal-section">
<h2>7. Lisans Talepleri</h2>
<p>İçeriklerinizin Platform'da lisanslı ve yasal olarak yayımlanmasını istiyorsanız lisans anlaşması için bizimle iletişime geçin:</p>
<p><a href="mailto:lisans@animexe.com" style="color:#ff2d7d">lisans@animexe.com</a></p>
</div>
<div class="legal-updated">Son güncelleme: Nisan 2025 Animexe Ekibi</div>
</div>
</div>
@endsection
@@ -0,0 +1,152 @@
@extends('frontend.layouts.app')
@section('title', 'Gizlilik Politikası | Animexe')
@push('styles')
<style>
.legal-hero{padding-top:calc(var(--nav-h) + 48px);padding-bottom:28px;text-align:center}
.legal-hero h1{font-size:1.8rem;font-weight:900;margin-bottom:8px}
.legal-hero p{color:rgba(255,255,255,.4);font-size:.9rem}
.legal-body{max-width:820px;margin:0 auto;padding:0 20px 80px}
.legal-section{margin-bottom:36px}
.legal-section h2{font-size:1.05rem;font-weight:800;color:#ff2d7d;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,.07)}
.legal-section p,.legal-section li{font-size:.88rem;color:rgba(255,255,255,.65);line-height:1.8;margin-bottom:8px}
.legal-section ul{padding-left:20px}
.legal-section li{margin-bottom:4px}
.legal-warn{background:rgba(59,130,246,.07);border:1px solid rgba(59,130,246,.2);border-radius:12px;padding:16px 20px;margin-bottom:32px}
.legal-warn p{color:rgba(180,210,255,.7);font-size:.83rem;margin:0;line-height:1.7}
.legal-table{width:100%;border-collapse:collapse;font-size:.83rem;margin-bottom:12px}
.legal-table th{text-align:left;color:rgba(255,255,255,.4);font-weight:700;padding:8px 12px;border-bottom:1px solid rgba(255,255,255,.07);font-size:.75rem;text-transform:uppercase}
.legal-table td{padding:8px 12px;color:rgba(255,255,255,.6);border-bottom:1px solid rgba(255,255,255,.04)}
.legal-updated{font-size:.78rem;color:rgba(255,255,255,.25);text-align:right;margin-bottom:32px}
</style>
@endpush
@section('content')
<div style="max-width:1200px;margin:0 auto">
<div class="legal-hero">
<h1>Gizlilik Politikası</h1>
<p>Son güncelleme: Nisan 2025</p>
</div>
<div class="legal-body">
<div class="legal-warn">
<p><strong>KVKK Bildirimi:</strong> Bu politika, 6698 sayılı Kişisel Verilerin Korunması Kanunu (KVKK) uyarınca hazırlanmıştır. Animexe olarak kişisel verilerinizin korunmasını ciddiye alıyoruz.</p>
</div>
<div class="legal-section">
<h2>1. Veri Sorumlusu</h2>
<p>Animexe platformu kapsamında kişisel verilerinizin işlenmesinden sorumlu veri sorumlusu, <strong>Animexe</strong>'dir. İletişim için: <a href="mailto:iletisim@animexe.com" style="color:#ff2d7d">iletisim@animexe.com</a></p>
</div>
<div class="legal-section">
<h2>2. Topladığımız Veriler</h2>
<table class="legal-table">
<tr><th>Veri Türü</th><th>Açıklama</th><th>Zorunluluk</th></tr>
<tr><td>Hesap Bilgileri</td><td>Ad, kullanıcı adı, e-posta adresi</td><td>Zorunlu</td></tr>
<tr><td>Profil Bilgileri</td><td>Profil fotoğrafı, biyografi (isteğe bağlı)</td><td>İsteğe bağlı</td></tr>
<tr><td>Aktivite Verileri</td><td>İzleme geçmişi, oy kullanma, yorum yazma, favori listesi</td><td>Otomatik</td></tr>
<tr><td>Teknik Veriler</td><td>IP adresi, tarayıcı türü, cihaz bilgisi, çerezler</td><td>Otomatik</td></tr>
<tr><td>İletişim Verileri</td><td>Bize gönderdiğiniz mesajlar</td><td>İsteğe bağlı</td></tr>
</table>
<p>Kredi kartı veya ödeme bilgisi toplamamaktayız.</p>
</div>
<div class="legal-section">
<h2>3. Verilerin İşlenme Amaçları</h2>
<ul>
<li>Hesabınızı oluşturmak ve yönetmek</li>
<li>Kişiselleştirilmiş içerik ve öneri sunmak (Ruh Hali Motoru vb.)</li>
<li>Kullanıcı güvenliğini sağlamak ve yetkisiz erişimi önlemek</li>
<li>Platform hizmetlerini geliştirmek ve teknik sorunları gidermek</li>
<li>İstatistiksel analiz yapmak (anonim ve toplu hâlde)</li>
<li>Hukuki yükümlülükleri yerine getirmek</li>
<li>Hizmet bildirimleri göndermek (e-posta onayı, şifre sıfırlama vb.)</li>
</ul>
</div>
<div class="legal-section">
<h2>4. Hukuki İşleme Dayanakları (KVKK md. 5)</h2>
<ul>
<li><strong>Sözleşme ifası:</strong> Hesap oluşturma ve platform hizmetlerinin sunulması için zorunlu veriler</li>
<li><strong>Meşru menfaat:</strong> Platform güvenliği, dolandırıcılık önleme, analitik</li>
<li><strong>Açık rıza:</strong> Pazarlama iletişimleri ve isteğe bağlı özellikler</li>
<li><strong>Hukuki yükümlülük:</strong> Resmi makam talepleri ve yasal gereklilikler</li>
</ul>
</div>
<div class="legal-section">
<h2>5. Verilerin Paylaşılması</h2>
<p>Kişisel verilerinizi aşağıdaki durumlar dışında üçüncü taraflarla satmaz veya kiralamayız:</p>
<ul>
<li><strong>Teknik hizmet sağlayıcılar:</strong> Sunucu barındırma, e-posta servisi gibi altyapı hizmetleri (işleme sözleşmesi kapsamında)</li>
<li><strong>Yasal zorunluluklar:</strong> Yetkili kamu kurum ve kuruluşlarının talepleri</li>
<li><strong>Platform güvenliği:</strong> Dolandırıcılık veya yasadışı faaliyetin tespiti halinde ilgili makamlarla paylaşım</li>
</ul>
<p>Üçüncü taraf embed içerik sağlayıcıları (video platformları) kendi gizlilik politikalarına tabidir; bu politikalar üzerinde kontrolümüz bulunmamaktadır.</p>
</div>
<div class="legal-section">
<h2>6. Çerezler (Cookies)</h2>
<p>Platform, aşağıdaki amaçlarla çerezler kullanmaktadır:</p>
<ul>
<li><strong>Zorunlu çerezler:</strong> Oturum yönetimi, CSRF koruması devre dışı bırakılamaz</li>
<li><strong>Tercih çerezleri:</strong> Dil, tema ve kullanıcı tercihleri</li>
<li><strong>Analitik çerezler:</strong> Anonim kullanım istatistikleri</li>
</ul>
<p>Tarayıcı ayarlarından çerezleri yönetebilirsiniz. Zorunlu çerezleri reddetmek Platform'un düzgün çalışmasını engelleyebilir.</p>
</div>
<div class="legal-section">
<h2>7. Veri Saklama Süreleri</h2>
<ul>
<li>Aktif hesap verileri: Hesap silinene kadar</li>
<li>Hesap silindikten sonra: 30 gün içinde kalıcı silme</li>
<li>Yasal zorunluluk gerektiren veriler: İlgili mevzuatta öngörülen süre (genellikle 310 yıl)</li>
<li>Anonimleştirilmiş analitik veriler: Süresiz (kişisel veri içermediğinden)</li>
</ul>
</div>
<div class="legal-section">
<h2>8. Veri Güvenliği</h2>
<p>Kişisel verilerinizi korumak için aşağıdaki teknik ve idari önlemleri alıyoruz:</p>
<ul>
<li>HTTPS (SSL/TLS) şifrelemesi</li>
<li>Şifreler bcrypt/hash ile saklanır; hiçbir zaman düz metin olarak tutulmaz</li>
<li>Veritabanı erişimini kısıtlayan güvenlik duvarları</li>
<li>Düzenli güvenlik güncellemeleri ve yama uygulaması</li>
<li>Çalışan erişim kontrolü (need-to-know prensibi)</li>
</ul>
<p>Hiçbir sistem %100 güvenli değildir. Güvenlik ihlali tespit edilmesi halinde sizi ve ilgili makamları KVKK'nın öngördüğü süre içinde (72 saat) bilgilendiririz.</p>
</div>
<div class="legal-section">
<h2>9. KVKK Kapsamındaki Haklarınız</h2>
<p>KVKK'nın 11. maddesi uyarınca aşağıdaki haklara sahipsiniz:</p>
<ul>
<li>Kişisel verilerinizin işlenip işlenmediğini öğrenme</li>
<li>İşlenmişse buna ilişkin bilgi talep etme</li>
<li>İşlenme amacını ve bunların amacına uygun kullanılıp kullanılmadığını öğrenme</li>
<li>Yurt içinde veya yurt dışında kişisel verilerin aktarıldığı üçüncü kişileri bilme</li>
<li>Kişisel verilerin eksik veya yanlış işlenmiş olması hâlinde bunların düzeltilmesini isteme</li>
<li>Verilerin silinmesini veya yok edilmesini isteme (yasal sınırlar çerçevesinde)</li>
<li>İşlenen verilerin münhasıran otomatik sistemler vasıtasıyla analiz edilmesi suretiyle aleyhinize bir sonucun ortaya çıkmasına itiraz etme</li>
<li>Kişisel verilerin kanuna aykırı olarak işlenmesi sebebiyle zarara uğraması hâlinde zararın giderilmesini talep etme</li>
</ul>
<p>Bu haklarınızı kullanmak için <a href="mailto:iletisim@animexe.com" style="color:#ff2d7d">iletisim@animexe.com</a> adresine e-posta gönderin. Talepler 30 gün içinde yanıtlanır.</p>
</div>
<div class="legal-section">
<h2>10. Politika Değişiklikleri</h2>
<p>Bu politikayı zaman zaman güncelleyebiliriz. Önemli değişiklikler için kayıtlı e-posta adresinize bildirim göndeririz. Değişikliği kabul etmiyorsanız hesabınızı silebilirsiniz.</p>
</div>
<div class="legal-section">
<h2>11. Şikâyet Hakkı</h2>
<p>Kişisel verilerinizin işlenmesiyle ilgili şikayetleriniz için <strong>Kişisel Verileri Koruma Kurumu</strong>'na (KVKK) başvurabilirsiniz: <a href="https://www.kvkk.gov.tr" target="_blank" rel="noopener" style="color:#ff2d7d">www.kvkk.gov.tr</a></p>
</div>
<div class="legal-updated">Son güncelleme: Nisan 2025 Animexe Ekibi</div>
</div>
</div>
@endsection
@@ -0,0 +1,141 @@
@extends('frontend.layouts.app')
@section('title', 'Kullanım Koşulları | Animexe')
@push('styles')
<style>
.legal-hero{padding-top:calc(var(--nav-h) + 48px);padding-bottom:28px;text-align:center}
.legal-hero h1{font-size:1.8rem;font-weight:900;margin-bottom:8px}
.legal-hero p{color:rgba(255,255,255,.4);font-size:.9rem}
.legal-body{max-width:820px;margin:0 auto;padding:0 20px 80px}
.legal-section{margin-bottom:36px}
.legal-section h2{font-size:1.05rem;font-weight:800;color:#ff2d7d;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,.07)}
.legal-section p,.legal-section li{font-size:.88rem;color:rgba(255,255,255,.65);line-height:1.8;margin-bottom:8px}
.legal-section ul{padding-left:20px}
.legal-section li{margin-bottom:4px}
.legal-warn{background:rgba(255,45,125,.07);border:1px solid rgba(255,45,125,.2);border-radius:12px;padding:16px 20px;margin-bottom:32px}
.legal-warn p{color:rgba(255,200,200,.7);font-size:.83rem;margin:0;line-height:1.7}
.legal-updated{font-size:.78rem;color:rgba(255,255,255,.25);text-align:right;margin-bottom:32px}
</style>
@endpush
@section('content')
<div style="max-width:1200px;margin:0 auto">
<div class="legal-hero">
<h1>Kullanım Koşulları</h1>
<p>Son güncelleme: Nisan 2025</p>
</div>
<div class="legal-body">
<div class="legal-warn">
<p><strong>Önemli:</strong> Bu platformu kullanarak aşağıdaki koşulların tamamını okuduğunuzu, anladığınızı ve kabul ettiğinizi beyan etmiş olursunuz. Koşulları kabul etmiyorsanız lütfen platformu kullanmayınız.</p>
</div>
<div class="legal-section">
<h2>1. Genel Bilgiler ve Platform Statüsü</h2>
<p>Animexe ("Platform", "Biz", "Site"), yalnızca <strong>tanıtım, eğitim ve araştırma amaçlı</strong> olarak tasarlanmış bir anime topluluğu ve içerik tanıtım platformudur. Platform, kullanıcılara anime hakkında bilgi, tartışma, yorum ve topluluğa dayalı özellikler sunmaktadır.</p>
<p>Platform, herhangi bir video içeriğini barındırmaz, depolamaz veya dağıtmaz. Gösterilen içerikler üçüncü taraf kaynaklardan embed yoluyla sunulmakta olup Platform yalnızca teknik bir aracı konumundadır.</p>
<p>Platform, 18 yaş ve üzeri kullanıcılara yönelik olup bazı içerikler yetişkin temalı unsurlar içerebilir. 18 yaşın altındaki kullanıcılar platformu kullanmamalıdır.</p>
</div>
<div class="legal-section">
<h2>2. Sorumluluk Reddi İçerik</h2>
<ul>
<li>Platform, üçüncü taraflarca üretilen veya barındırılan içeriklerden <strong>hiçbir şekilde sorumlu değildir</strong>.</li>
<li>Embed edilen videolar, resimler veya diğer medya içerikleri ilgili üçüncü taraf platformlara (örn. Google Drive, Dailymotion, Streamtape, vb.) aittir.</li>
<li>Platform, üçüncü taraf içeriklerinin doğruluğunu, güncelliğini veya yasallığını garanti etmez.</li>
<li>Herhangi bir içeriğin telif hakkı ihlali oluşturduğunu düşünüyorsanız lütfen <a href="{{ route('legal.dmca') }}" style="color:#ff2d7d">Telif Hakkı Bildirimi</a> sayfamızı ziyaret edin.</li>
<li>Platform, yalnızca teknik altyapı sağlayan bir aracı niteliğinde olup 5651 sayılı Kanun kapsamında içerik sağlayıcı değil, <strong>yer sağlayıcı</strong> konumundadır.</li>
</ul>
</div>
<div class="legal-section">
<h2>3. Kullanıcı Hesabı ve Sorumlulukları</h2>
<ul>
<li>Kayıt olurken gerçek ve güncel bilgi sağlamak zorundasınız. Yanlış bilgi vermek hesabın askıya alınmasına yol açabilir.</li>
<li>Hesap şifrenizin güvenliğinden siz sorumlusunuz. Hesabınızın yetkisiz kullanımını derhal bize bildirmeniz gerekmektedir.</li>
<li>Bir kullanıcı, yalnızca bir hesap açabilir. Çoklu hesap açmak yasaktır.</li>
<li>Hesabınızı başkasına devredemez veya satamazsınız.</li>
</ul>
</div>
<div class="legal-section">
<h2>4. Yasaklı Kullanımlar</h2>
<p>Aşağıdaki eylemler kesinlikle yasaktır:</p>
<ul>
<li>Platform altyapısına zarar verecek saldırılar (DDoS, SQL injection, XSS vb.) gerçekleştirmek</li>
<li>Otomatik bot, scraper veya benzeri araçlarla Platform'a erişmek</li>
<li>Diğer kullanıcılara yönelik taciz, nefret söylemi, tehdit veya ayrımcılık içeren içerik paylaşmak</li>
<li>Gerçek kişilerin kişisel bilgilerini izinsiz olarak paylaşmak (doxing)</li>
<li>Yasadışı faaliyetleri teşvik eden içerik paylaşmak</li>
<li>Platform'un ticari amaçlarla yetkisiz kullanımı</li>
<li>Spam, yanıltıcı veya aldatıcı içerik paylaşmak</li>
<li>Reşit olmayan bireyleri içeren müstehcen içerik paylaşmak veya yüklemeye teşebbüs etmek</li>
</ul>
</div>
<div class="legal-section">
<h2>5. Kullanıcı Tarafından Oluşturulan İçerik (UGC)</h2>
<p>Platform'a yorum, argüman, kapsül mesajı veya diğer içerikler gönderdiğinizde:</p>
<ul>
<li>Bu içeriklerin yasalara uygun, doğru ve başkalarının haklarına saygılı olduğunu beyan edersiniz.</li>
<li>Platform'a, içeriği görüntüleme, depolama ve kullanıcılara sunma için <strong>gayri münhasır, ücretsiz bir lisans</strong> verirsiniz.</li>
<li>Üçüncü tarafların telif hakları, ticari markaları veya özel haklarını ihlal eden içerik paylaşmaktan siz sorumlusunuz.</li>
<li>Platform, herhangi bir içeriği önceden haber vermeksizin kaldırma hakkını saklı tutar.</li>
<li>Spoiler tespiti dahil her türlü içerik moderasyonu otomatik sistemler ve/veya editörler tarafından yapılabilir; Platform bu kararların kesin doğruluğunu garanti etmez.</li>
</ul>
</div>
<div class="legal-section">
<h2>6. Fikri Mülkiyet</h2>
<p>Platform'un özgün tasarımı, logosu, kodu ve içerikleri Animexe'ye aittir ve Türk Fikir ve Sanat Eserleri Kanunu kapsamında koruma altındadır.</p>
<p>Anime adları, görseller, karakterler ve ilgili materyaller kendi telif hakkı sahiplerine (Japon stüdyolar, yayıncılar vb.) aittir. Bu içeriklerin kullanımı yalnızca tanıtım amacıyla "adil kullanım" ilkesi çerçevesinde gerçekleşmektedir.</p>
<p>Platform içeriğini izinsiz kopyalamak, dağıtmak veya ticari amaçla kullanmak yasaktır.</p>
</div>
<div class="legal-section">
<h2>7. Hesap Askıya Alma ve Sonlandırma</h2>
<p>Platform, aşağıdaki durumlarda hesabınızı önceden haber vermeksizin askıya alabilir veya silebilir:</p>
<ul>
<li>Bu Koşullar'ın ihlali</li>
<li>Hukuki bir gereklilik veya resmi makam talebi</li>
<li>Diğer kullanıcılara veya platforma zarar veren davranışlar</li>
<li>Uzun süreli hesap hareketsizliği (12 ay ve üzeri)</li>
</ul>
<p>Hesabınızı kendiniz silmek istediğinizde tüm verileriniz 30 gün içinde kalıcı olarak silinir.</p>
</div>
<div class="legal-section">
<h2>8. Hizmetin Değiştirilmesi ve Sonlandırılması</h2>
<p>Platform, herhangi bir zamanda ve önceden bildirimde bulunmaksızın hizmetini, özelliklerini veya bu Koşulları değiştirme hakkını saklı tutar. Değişiklikler yayımlandıktan sonra platformu kullanmaya devam etmeniz yeni koşulları kabul ettiğiniz anlamına gelir.</p>
<p>Platform, teknik arıza, bakım veya mücbir sebep nedeniyle hizmetini geçici ya da kalıcı olarak durdurabilir. Bu durumdan doğan hiçbir zarardan sorumlu tutulamaz.</p>
</div>
<div class="legal-section">
<h2>9. Sorumluluk Sınırlaması</h2>
<p>Platform, aşağıdaki durumlardan kaynaklanan zararlardan sorumlu değildir:</p>
<ul>
<li>Üçüncü taraf içeriklere erişimden veya bu içeriklere güvenmekten kaynaklanan zararlar</li>
<li>Hizmet kesintisi veya veri kaybı</li>
<li>Kullanıcılar arasındaki anlaşmazlıklar</li>
<li>Yetkisiz hesap erişimi (kullanıcı güvenlik ihlalinden kaynaklanıyorsa)</li>
<li>Üçüncü taraf site veya hizmetlerinin kullanımı</li>
</ul>
<p>Platformun toplam sorumluluğu, varsa ödenen üyelik ücretini geçemez.</p>
</div>
<div class="legal-section">
<h2>10. Uygulanacak Hukuk ve Uyuşmazlık Çözümü</h2>
<p>Bu Koşullar, <strong>Türkiye Cumhuriyeti</strong> hukukuna tabidir. Bu Koşullar'dan doğan uyuşmazlıkların çözümünde <strong>Türkiye Cumhuriyeti mahkemeleri</strong> yetkilidir.</p>
<p>Herhangi bir anlaşmazlık için öncelikle <a href="mailto:iletisim@animexe.com" style="color:#ff2d7d">iletisim@animexe.com</a> adresine başvurulması tavsiye edilir.</p>
</div>
<div class="legal-section">
<h2>11. İletişim</h2>
<p>Kullanım Koşulları hakkında sorularınız için: <a href="mailto:iletisim@animexe.com" style="color:#ff2d7d">iletisim@animexe.com</a></p>
</div>
<div class="legal-updated">Son güncelleme: Nisan 2025 Animexe Ekibi</div>
</div>
</div>
@endsection
@@ -0,0 +1,103 @@
@extends('frontend.layouts.app')
@section('title', 'Mesajlar — Animexe')
@push('styles')
<style>
.msg-wrap{max-width:680px;margin:0 auto;padding:calc(var(--nav-h) + 28px) 16px 64px}
.msg-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}
.msg-header h1{font-size:1.3rem;font-weight:900;color:#fff}
.msg-list{display:flex;flex-direction:column;gap:4px}
.msg-row{
display:flex;align-items:center;gap:14px;
padding:14px 16px;border-radius:14px;
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.06);
text-decoration:none;transition:all .15s;cursor:pointer;
}
.msg-row:hover{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.12)}
.msg-row.unread{background:rgba(0,245,255,.04);border-color:rgba(0,245,255,.12)}
.msg-ava{
width:46px;height:46px;border-radius:50%;flex-shrink:0;overflow:hidden;
background:linear-gradient(135deg,var(--accent2),var(--accent));
display:flex;align-items:center;justify-content:center;
font-size:1.1rem;font-weight:800;color:#fff;
}
.msg-ava img{width:100%;height:100%;object-fit:cover}
.msg-info{flex:1;min-width:0}
.msg-name{font-size:.9rem;font-weight:700;color:#fff;margin-bottom:3px;display:flex;align-items:center;gap:8px}
.msg-preview{font-size:.8rem;color:rgba(255,255,255,.4);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.msg-row.unread .msg-preview{color:rgba(255,255,255,.7)}
.msg-unread-dot{width:8px;height:8px;border-radius:50%;background:var(--accent2);flex-shrink:0}
.msg-time{font-size:.7rem;color:rgba(255,255,255,.25);flex-shrink:0;align-self:flex-start;margin-top:4px}
.msg-empty{text-align:center;padding:64px 20px;color:rgba(255,255,255,.2)}
.msg-empty i{font-size:2.8rem;display:block;margin-bottom:14px;opacity:.2}
.msg-empty p{font-size:.9rem}
</style>
@endpush
@section('content')
<div class="msg-wrap">
<div class="msg-header">
<h1><i class="bi bi-chat-dots" style="color:var(--accent2)"></i> Mesajlar</h1>
</div>
@if($conversations->isEmpty())
<div class="msg-empty">
<i class="bi bi-chat-square-dots"></i>
<p>Henüz mesajın yok.<br>Bir kullanıcının profiline git ve mesaj gönder.</p>
</div>
@else
<div class="msg-list">
@foreach($conversations as $conv)
@php
$other = $conv['other'];
$last = $conv['last_message'];
$unread= $conv['unread'];
@endphp
<a href="{{ route('messages.show', $conv['id']) }}" class="msg-row {{ $unread > 0 ? 'unread' : '' }}">
<div class="msg-ava">
@if($other?->avatar)
<img src="{{ \App\Support\MediaUrl::fromStoragePath($other->avatar) }}" alt="{{ $other->name }}">
@else
{{ mb_strtoupper(mb_substr($other?->name ?? '?', 0, 1)) }}
@endif
</div>
<div class="msg-info">
<div class="msg-name">
{{ $other?->name ?? 'Silinmiş Kullanıcı' }}
@if($unread > 0)
<span style="font-size:.65rem;background:var(--accent2);color:#05050e;padding:1px 7px;border-radius:20px;font-weight:800">{{ $unread }}</span>
@endif
</div>
<div class="msg-preview">
@if($last)
@if($last->user_id === auth()->id()) <span style="color:rgba(255,255,255,.25)">Sen: </span> @endif
@if(str_starts_with($last->body, 'ANIMESHARE::'))
@php try { $sd = json_decode(substr($last->body,12),true); } catch(\Throwable $e){ $sd=null; } @endphp
<i class="bi bi-play-circle" style="color:var(--accent2)"></i> {{ $sd['title'] ?? 'Anime' }} paylaştı
@elseif(str_starts_with($last->body, 'IMAGE::'))
<i class="bi bi-image"></i> Fotoğraf
@elseif(str_starts_with($last->body, 'GIF::'))
<i class="bi bi-file-play"></i> GIF
@else
{{ Str::limit($last->body, 60) }}
@endif
@else
<em>Henüz mesaj yok</em>
@endif
</div>
</div>
<div style="display:flex;flex-direction:column;align-items:flex-end;gap:6px">
@if($unread > 0)<div class="msg-unread-dot"></div>@endif
<span class="msg-time">
@if($conv['updated_at']){{ $conv['updated_at']->diffForHumans(null, true) }}@endif
</span>
</div>
</a>
@endforeach
</div>
@endif
</div>
@endsection
@@ -0,0 +1,655 @@
@extends('frontend.layouts.app')
@section('title', ($other?->name ?? 'Mesaj') . ' — Animexe')
@push('styles')
<style>
footer{display:none!important}
#page-content{padding:0!important}
#ai-fab,#ai-panel{display:none!important}
#topnav,#mob-bottom-nav{display:none!important}
.w-chat-support{display:none!important}
.chat-root{
display:flex;flex-direction:column;
position:fixed;
top:0;left:0;right:0;bottom:0;
background:#05050e;
}
/* ── Header ─────────────────────────────── */
.chat-hdr{
display:flex;align-items:center;gap:10px;
padding:10px 14px;
background:rgba(5,5,14,.95);backdrop-filter:blur(12px);
border-bottom:1px solid rgba(255,255,255,.07);
flex-shrink:0;z-index:10;
}
.chat-hdr-back{
width:36px;height:36px;border-radius:10px;
display:flex;align-items:center;justify-content:center;
background:rgba(255,255,255,.06);border:none;
color:rgba(255,255,255,.6);font-size:1rem;cursor:pointer;
text-decoration:none;flex-shrink:0;transition:all .15s;
}
.chat-hdr-back:hover{background:rgba(255,255,255,.12);color:#fff}
.chat-hdr-ava{
width:38px;height:38px;border-radius:50%;overflow:hidden;flex-shrink:0;
background:linear-gradient(135deg,var(--accent2),var(--accent));
display:flex;align-items:center;justify-content:center;
font-size:.9rem;font-weight:800;color:#fff;text-decoration:none;
}
.chat-hdr-ava img{width:100%;height:100%;object-fit:cover}
.chat-hdr-info{flex:1;min-width:0}
.chat-hdr-name{font-size:.9rem;font-weight:800;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chat-hdr-sub{font-size:.68rem;color:rgba(255,255,255,.3)}
.chat-hdr-btn{
width:36px;height:36px;border-radius:10px;flex-shrink:0;
display:flex;align-items:center;justify-content:center;
background:rgba(255,255,255,.06);border:none;color:rgba(255,255,255,.5);
font-size:.9rem;cursor:pointer;text-decoration:none;transition:all .15s;
}
.chat-hdr-btn:hover{background:rgba(255,255,255,.12);color:#fff}
.chat-hdr-btn.call{color:#22c55e}
.chat-hdr-btn.call:hover{background:rgba(34,197,94,.15);color:#22c55e}
/* ── Messages area ──────────────────────── */
.chat-body{
flex:1;overflow-y:auto;
padding:14px 14px 8px;
display:flex;flex-direction:column;
gap:4px;
-webkit-overflow-scrolling:touch;
min-height:0;
}
.chat-body::-webkit-scrollbar{width:3px}
.chat-body::-webkit-scrollbar-thumb{background:rgba(255,255,255,.1);border-radius:2px}
/* ── Bubbles ────────────────────────────── */
.chat-msg{display:flex;gap:8px;max-width:80%;align-items:flex-end}
.chat-msg.mine{align-self:flex-end;flex-direction:row-reverse}
.chat-msg.theirs{align-self:flex-start}
.chat-msg-ava{
width:28px;height:28px;border-radius:50%;overflow:hidden;flex-shrink:0;
background:linear-gradient(135deg,var(--accent2),var(--accent));
display:flex;align-items:center;justify-content:center;
font-size:.65rem;font-weight:800;color:#fff;
}
.chat-msg-ava img{width:100%;height:100%;object-fit:cover}
.chat-msg.mine .chat-msg-ava{display:none}
.chat-bubble{
padding:9px 14px;border-radius:18px;
font-size:.88rem;line-height:1.5;word-break:break-word;max-width:100%;
}
.chat-msg.mine .chat-bubble{
background:var(--accent2);color:#05050e;font-weight:500;border-bottom-right-radius:5px;
}
.chat-msg.theirs .chat-bubble{
background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.08);
color:rgba(255,255,255,.9);border-bottom-left-radius:5px;
}
.chat-time{font-size:.6rem;color:rgba(255,255,255,.25);align-self:flex-end;margin-bottom:3px;flex-shrink:0}
.chat-date-sep{
text-align:center;font-size:.65rem;color:rgba(255,255,255,.2);
padding:12px 0 4px;font-weight:600;letter-spacing:.08em;
}
/* ── Media messages ─────────────────────── */
.chat-img{
max-width:220px;max-height:220px;border-radius:14px;
cursor:pointer;display:block;object-fit:cover;
transition:opacity .15s;border:1px solid rgba(255,255,255,.08);
}
.chat-img:hover{opacity:.88}
.chat-gif{max-width:220px;border-radius:14px;display:block}
/* ── Share card ─────────────────────────── */
.share-card{
display:flex;gap:10px;align-items:flex-start;
background:rgba(0,245,255,.06);border:1px solid rgba(0,245,255,.18);
border-radius:14px;padding:10px 12px;text-decoration:none;
max-width:260px;transition:all .15s;
}
.share-card:hover{background:rgba(0,245,255,.12);border-color:rgba(0,245,255,.35)}
.share-card-img{width:54px;height:78px;border-radius:8px;overflow:hidden;flex-shrink:0;background:rgba(255,255,255,.05)}
.share-card-img img{width:100%;height:100%;object-fit:cover;display:block}
.share-card-label{font-size:.58rem;color:var(--accent2);font-weight:700;letter-spacing:.08em;text-transform:uppercase;margin-bottom:4px}
.share-card-title{font-size:.8rem;font-weight:800;color:#fff;line-height:1.3;margin-bottom:3px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.share-card-ep{font-size:.7rem;color:rgba(255,255,255,.5);margin-bottom:3px}
.share-card-play{font-size:.68rem;color:var(--accent2);display:flex;align-items:center;gap:3px;margin-top:4px;font-weight:700}
/* ── Footer input ───────────────────────── */
.chat-footer{
padding:8px 10px;
background:rgba(5,5,14,.95);backdrop-filter:blur(12px);
border-top:1px solid rgba(255,255,255,.07);
flex-shrink:0;
padding-bottom:max(8px,env(safe-area-inset-bottom));
}
.chat-toolbar{
display:flex;align-items:center;gap:6px;
padding:4px 0 8px;
border-bottom:1px solid rgba(255,255,255,.06);
margin-bottom:6px;
}
.chat-tool-btn{
width:38px;height:38px;border-radius:10px;border:none;
background:rgba(255,255,255,.06);color:rgba(255,255,255,.45);
font-size:.88rem;cursor:pointer;
display:flex;align-items:center;justify-content:center;
transition:all .15s;flex-shrink:0;
-webkit-tap-highlight-color:transparent;
}
.chat-tool-btn:hover{background:rgba(255,255,255,.13);color:#fff}
.chat-tool-btn.active{background:rgba(0,245,255,.12);color:var(--accent2)}
.chat-form{display:flex;align-items:flex-end;gap:8px}
.chat-input{
flex:1;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.1);
border-radius:20px;padding:10px 16px;color:#fff;font-size:.9rem;font-family:inherit;
resize:none;max-height:120px;overflow-y:auto;min-height:42px;
line-height:1.4;outline:none;-webkit-appearance:none;transition:border-color .15s;
}
.chat-input:focus{border-color:rgba(0,245,255,.35)}
.chat-input::placeholder{color:rgba(255,255,255,.25)}
.chat-send{
width:42px;height:42px;border-radius:50%;flex-shrink:0;
background:var(--accent2);border:none;color:#05050e;
font-size:1rem;cursor:pointer;
display:flex;align-items:center;justify-content:center;
transition:all .15s;-webkit-tap-highlight-color:transparent;
}
.chat-send:hover{background:#00d4de;transform:scale(1.05)}
.chat-send:disabled{opacity:.4;transform:none;cursor:default}
/* ── Emoji picker ───────────────────────── */
#chat-emoji-picker{
display:none;flex-wrap:wrap;gap:2px;
background:rgba(10,10,22,.98);border:1px solid rgba(255,255,255,.1);
border-radius:14px;padding:10px;
max-height:180px;overflow-y:auto;margin-bottom:6px;
scrollbar-width:thin;
}
#chat-emoji-picker.show{display:flex}
.chat-emoji-btn{
font-size:1.3rem;width:36px;height:36px;
display:flex;align-items:center;justify-content:center;
border:none;background:none;cursor:pointer;border-radius:8px;
transition:background .12s;-webkit-tap-highlight-color:transparent;
}
.chat-emoji-btn:hover{background:rgba(255,255,255,.1)}
/* ── GIF picker ─────────────────────────── */
#chat-gif-picker{
display:none;
background:rgba(10,10,22,.98);border:1px solid rgba(255,255,255,.1);
border-radius:14px;padding:8px;margin-bottom:6px;
}
#chat-gif-picker.show{display:block}
.chat-gif-search{
width:100%;padding:8px 12px;border-radius:10px;
background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);
color:#fff;font-size:.82rem;font-family:inherit;outline:none;
margin-bottom:8px;-webkit-appearance:none;
}
.chat-gif-search::placeholder{color:rgba(255,255,255,.3)}
.chat-gif-grid{
display:grid;grid-template-columns:repeat(3,1fr);gap:4px;
max-height:160px;overflow-y:auto;
}
.chat-gif-item{
aspect-ratio:1;background:rgba(255,255,255,.05);border-radius:8px;
overflow:hidden;cursor:pointer;transition:opacity .15s;
}
.chat-gif-item:hover{opacity:.8}
.chat-gif-item img{width:100%;height:100%;object-fit:cover;display:block}
.chat-gif-ph{padding:20px;text-align:center;color:rgba(255,255,255,.25);font-size:.78rem}
.chat-empty{
flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
color:rgba(255,255,255,.2);gap:10px;
}
.chat-empty i{font-size:2.4rem;opacity:.2}
.chat-empty p{font-size:.85rem}
@media(min-width:640px){
.chat-root{max-width:760px;left:50%;transform:translateX(-50%)}
.chat-msg{max-width:72%}
}
@media(max-width:640px){
.chat-msg{max-width:88%}
}
</style>
@endpush
@section('content')
@php $me = auth()->user(); @endphp
<div class="chat-root">
{{-- Header --}}
<div class="chat-hdr">
<a href="{{ route('messages.index') }}" class="chat-hdr-back">
<i class="bi bi-chevron-left"></i>
</a>
<a href="{{ $other ? route('user.profile', $other) : '#' }}" class="chat-hdr-ava">
@if($other?->avatar)
<img src="{{ \App\Support\MediaUrl::fromStoragePath($other->avatar) }}" alt="{{ $other->name }}">
@else
{{ mb_strtoupper(mb_substr($other?->name ?? '?', 0, 1)) }}
@endif
</a>
<div class="chat-hdr-info">
<div class="chat-hdr-name">{{ $other?->name ?? 'Silinmiş Kullanıcı' }}</div>
@if($other?->username)
<div class="chat-hdr-sub">@php echo '@' . e($other->username); @endphp</div>
@endif
</div>
@if($other)
{{-- Sesli arama --}}
<button class="chat-hdr-btn call" id="call-btn" data-user-id="{{ $other->id }}" title="Sesli ara">
<i class="bi bi-telephone-fill"></i>
</button>
{{-- Profil --}}
<a href="{{ route('user.profile', $other) }}" class="chat-hdr-btn" title="Profil">
<i class="bi bi-person"></i>
</a>
@endif
</div>
{{-- Messages --}}
<div class="chat-body" id="chat-body">
@if($messages->isEmpty())
<div class="chat-empty">
<i class="bi bi-chat-dots"></i>
<p>Henüz mesaj yok. İlk mesajı sen gönder!</p>
</div>
@else
@php $lastDate = null; @endphp
@foreach($messages as $msg)
@php
$msgDate = $msg->created_at->toDateString();
$isMine = $msg->user_id === $me->id;
@endphp
@if($msgDate !== $lastDate)
<div class="chat-date-sep">{{ $msg->created_at->translatedFormat('d F Y') }}</div>
@php $lastDate = $msgDate; @endphp
@endif
<div class="chat-msg {{ $isMine ? 'mine' : 'theirs' }}" data-id="{{ $msg->id }}">
@if(!$isMine)
<div class="chat-msg-ava">
@if($msg->user?->avatar)
<img src="{{ \App\Support\MediaUrl::fromStoragePath($msg->user->avatar) }}" alt="">
@else{{ mb_strtoupper(mb_substr($msg->user?->name ?? '?', 0, 1)) }}@endif
</div>
@endif
<div>
@if(str_starts_with($msg->body, 'ANIMESHARE::'))
@php try { $sd = json_decode(substr($msg->body,12),true); } catch(\Throwable $e){ $sd=null; } @endphp
@if($sd)
<a href="{{ url('/watch/'.($sd['slug']??'').'/'.($sd['s']??1).'/'.($sd['e']??1).(($sd['ts']??0)>0?'?t='.$sd['ts']:'')) }}" class="share-card">
<div class="share-card-img">
@if(!empty($sd['cover']))<img src="{{ $sd['cover'] }}" loading="lazy" alt="">@else<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:rgba(255,255,255,.2);font-size:1.4rem"><i class="bi bi-play-circle"></i></div>@endif
</div>
<div>
<div class="share-card-label"><i class="bi bi-play-fill"></i> Anime Bölümü</div>
<div class="share-card-title">{{ $sd['title'] ?? '' }}</div>
<div class="share-card-ep">{{ $sd['s'] ?? '' }}. Sezon {{ $sd['e'] ?? '' }}. Bölüm</div>
<div class="share-card-play"><i class="bi bi-play-circle-fill"></i> İzle</div>
</div>
</a>
@else
<div class="chat-bubble">{{ $msg->body }}</div>
@endif
@elseif(str_starts_with($msg->body, 'IMAGE::'))
<img src="{{ substr($msg->body, 7) }}" class="chat-img" loading="lazy" alt="Resim"
onclick="window.open('{{ substr($msg->body, 7) }}','_blank')">
@elseif(str_starts_with($msg->body, 'GIF::'))
<img src="{{ substr($msg->body, 5) }}" class="chat-gif" loading="lazy" alt="GIF">
@else
<div class="chat-bubble">{{ $msg->body }}</div>
@endif
</div>
<span class="chat-time">{{ $msg->created_at->format('H:i') }}</span>
</div>
@endforeach
@endif
</div>
{{-- Footer --}}
<div class="chat-footer">
<div id="chat-emoji-picker"></div>
<div id="chat-gif-picker">
<input type="text" class="chat-gif-search" id="chat-gif-input" placeholder="GIF ara…">
<div class="chat-gif-grid" id="chat-gif-grid">
<div class="chat-gif-ph">GIF aramak için yaz</div>
</div>
</div>
<div class="chat-toolbar">
<button class="chat-tool-btn" id="chat-emoji-btn" title="Emoji" style="display:none">
<i class="bi bi-emoji-smile"></i>
</button>
<button class="chat-tool-btn" id="chat-gif-btn" title="GIF">
<span style="font-size:.65rem;font-weight:800;letter-spacing:.03em">GIF</span>
</button>
<label class="chat-tool-btn" title="Resim gönder" style="cursor:pointer">
<i class="bi bi-image"></i>
<input type="file" id="chat-img-input" accept="image/*" style="display:none">
</label>
<div style="flex:1"></div>
</div>
<div class="chat-form">
<textarea class="chat-input" id="chat-input"
placeholder="Mesaj yaz..." rows="1" maxlength="2000"
autocomplete="off" autocorrect="off"></textarea>
<button type="button" class="chat-send" id="chat-send">
<i class="bi bi-send-fill"></i>
</button>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
// Crisp ve diğer overlay widgetları kapat
try { $crisp.push(['do','chat:hide']); } catch(_){}
document.addEventListener('DOMContentLoaded', function(){
try { $crisp.push(['do','chat:hide']); } catch(_){}
// topnav/bottom-nav kesinlikle gizle (layout JS override için)
const tn = document.getElementById('topnav');
const bn = document.getElementById('mob-bottom-nav');
if(tn) tn.style.setProperty('display','none','important');
if(bn) bn.style.setProperty('display','none','important');
});
</script>
<script>
(function(){
'use strict';
const SEND_URL = '{{ route("messages.send", $conversation) }}';
const POLL_URL = '{{ route("messages.poll", $conversation) }}';
const UPLOAD_URL = '{{ route("messages.upload-image") }}';
const TENOR_KEY = '{{ env("TENOR_API_KEY","") }}';
const CSRF = document.querySelector('meta[name=csrf-token]')?.content || '';
const ME_ID = {{ $me->id }};
const MY_AVA = {!! json_encode($me->avatar ? \App\Support\MediaUrl::fromStoragePath($me->avatar) : null) !!};
const MY_NAME = {!! json_encode($me->name) !!};
const IS_DESKTOP = window.innerWidth >= 640;
let lastId = {{ $messages->last()?->id ?? 0 }};
let polling = null;
let emojiOpen = false;
let gifOpen = false;
let gifTimer = null;
const chatBody = document.getElementById('chat-body');
const inputEl = document.getElementById('chat-input');
const sendBtn = document.getElementById('chat-send');
/* ── Scroll ──────────────────────────────── */
function scrollBottom(force = false) {
const near = chatBody.scrollHeight - chatBody.scrollTop - chatBody.clientHeight < 160;
if (force || near) chatBody.scrollTop = chatBody.scrollHeight;
}
scrollBottom(true);
/* ── Input auto-grow ─────────────────────── */
inputEl.addEventListener('input', () => {
inputEl.style.height = 'auto';
inputEl.style.height = Math.min(inputEl.scrollHeight, 120) + 'px';
if (inputEl.value) { hideEmoji(); hideGif(); }
});
inputEl.addEventListener('keydown', e => {
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); doSend(); }
});
sendBtn.addEventListener('click', doSend);
/* ── Escape chars ────────────────────────── */
function escH(s) {
return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
/* ── Render message body ─────────────────── */
function renderBody(body) {
if (!body) return '<div class="chat-bubble"></div>';
if (body.startsWith('ANIMESHARE::')) {
try {
const d = JSON.parse(body.slice(12));
const url = `/watch/${d.slug}/${d.s}/${d.e}${d.ts>0?'?t='+d.ts:''}`;
const img = d.cover
? `<img src="${escH(d.cover)}" loading="lazy" alt="">`
: `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:rgba(255,255,255,.2)"><i class="bi bi-play-circle" style="font-size:1.4rem"></i></div>`;
return `<a href="${escH(url)}" class="share-card">
<div class="share-card-img">${img}</div>
<div>
<div class="share-card-label"><i class="bi bi-play-fill"></i> Anime Bölümü</div>
<div class="share-card-title">${escH(d.title||'')}</div>
<div class="share-card-ep">${escH(String(d.s))}. Sezon ${escH(String(d.e))}. Bölüm</div>
<div class="share-card-play"><i class="bi bi-play-circle-fill"></i> İzle</div>
</div>
</a>`;
} catch(_) {}
}
if (body.startsWith('IMAGE::')) {
const url = escH(body.slice(7));
return `<img src="${url}" class="chat-img" loading="lazy" alt="Resim" onclick="window.open('${url}','_blank')">`;
}
if (body.startsWith('GIF::')) {
return `<img src="${escH(body.slice(5))}" class="chat-gif" loading="lazy" alt="GIF">`;
}
return `<div class="chat-bubble">${escH(body)}</div>`;
}
/* ── Append message to DOM ───────────────── */
function appendMsg(id, body, isMine, time, avatar, name) {
if (document.querySelector(`.chat-msg[data-id="${id}"]`)) return;
document.querySelector('.chat-empty')?.remove();
const wrap = document.createElement('div');
wrap.className = 'chat-msg ' + (isMine ? 'mine' : 'theirs');
wrap.dataset.id = id;
let ava = '';
if (!isMine) {
ava = avatar
? `<div class="chat-msg-ava"><img src="${escH(avatar)}" alt=""></div>`
: `<div class="chat-msg-ava">${escH((name||'?').charAt(0).toUpperCase())}</div>`;
}
wrap.innerHTML = ava + `<div>${renderBody(body)}</div><span class="chat-time">${escH(time)}</span>`;
chatBody.appendChild(wrap);
scrollBottom();
}
/* ── Send text ───────────────────────────── */
async function doSend() {
const text = inputEl.value.trim();
if (!text) return;
sendBtn.disabled = true;
inputEl.value = '';
inputEl.style.height = 'auto';
try {
const res = await fetch(SEND_URL, {
method: 'POST',
headers: {'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json', 'Content-Type': 'application/json'},
body: JSON.stringify({ body: text }),
});
if (!res.ok) throw new Error();
const d = await res.json();
appendMsg(d.id, d.body, true, d.created_at, MY_AVA, MY_NAME);
if (d.id > lastId) lastId = d.id;
scrollBottom(true);
} catch(e) {
inputEl.value = text;
}
sendBtn.disabled = false;
inputEl.focus();
}
/* ── Send image ──────────────────────────── */
document.getElementById('chat-img-input')?.addEventListener('change', async function() {
const file = this.files[0];
if (!file) return;
this.value = '';
try {
const fd = new FormData();
fd.append('image', file);
const r = await fetch(UPLOAD_URL, {
method: 'POST',
headers: {'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json'},
body: fd,
});
const d = await r.json();
if (!d.url) throw new Error();
const r2 = await fetch(SEND_URL, {
method: 'POST',
headers: {'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json', 'Content-Type': 'application/json'},
body: JSON.stringify({ body: 'IMAGE::' + d.url }),
});
const d2 = await r2.json();
appendMsg(d2.id, d2.body, true, d2.created_at, MY_AVA, MY_NAME);
if (d2.id > lastId) lastId = d2.id;
scrollBottom(true);
} catch(e) {
if (window.showToast) showToast('Resim gönderilemedi.', 'error');
}
});
/* ── Send GIF ────────────────────────────── */
window.chatSendGif = async function(gifUrl) {
hideGif();
try {
const r = await fetch(SEND_URL, {
method: 'POST',
headers: {'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json', 'Content-Type': 'application/json'},
body: JSON.stringify({ body: 'GIF::' + gifUrl }),
});
const d = await r.json();
appendMsg(d.id, d.body, true, d.created_at, MY_AVA, MY_NAME);
if (d.id > lastId) lastId = d.id;
scrollBottom(true);
} catch(_) {}
};
/* ── Polling ─────────────────────────────── */
async function poll() {
try {
const res = await fetch(`${POLL_URL}?after=${lastId}`, {
headers: {'Accept': 'application/json', 'X-CSRF-TOKEN': CSRF}
});
const d = await res.json();
d.messages.forEach(m => {
if (m.user_id !== ME_ID) {
appendMsg(m.id, m.body, false, m.created_at, m.avatar, m.name);
}
if (m.id > lastId) lastId = m.id;
});
} catch(_) {}
}
function startPoll() { polling = setInterval(poll, 3000); }
function stopPoll() { clearInterval(polling); polling = null; }
document.addEventListener('visibilitychange', () => document.hidden ? stopPoll() : startPoll());
startPoll();
/* ── Emoji picker ────────────────────────── */
const EMOJIS = '😀😂😅😊😍🥰😘😎😭😤😡🥺🤔😴🤩🥳😱🙈🙄😏🤗😬😵👍👎❤️🔥💯✨🎉🎊🎮🎬🎵🍕🍜🍣🎃👻🦊🐱🐶🐼🌸🌙⭐🌈💎🚀🏆🎯🔮';
function initEmoji() {
const p = document.getElementById('chat-emoji-picker');
if (!p || p.dataset.built) return;
p.dataset.built = '1';
[...EMOJIS].forEach(em => {
const btn = document.createElement('button');
btn.className = 'chat-emoji-btn';
btn.textContent = em;
btn.addEventListener('click', () => {
const pos = inputEl.selectionStart;
inputEl.value = inputEl.value.slice(0, pos) + em + inputEl.value.slice(inputEl.selectionEnd);
inputEl.selectionStart = inputEl.selectionEnd = pos + em.length;
inputEl.dispatchEvent(new Event('input'));
inputEl.focus();
});
p.appendChild(btn);
});
}
function toggleEmoji() {
emojiOpen = !emojiOpen;
if (emojiOpen) { hideGif(); initEmoji(); }
document.getElementById('chat-emoji-picker').classList.toggle('show', emojiOpen);
document.getElementById('chat-emoji-btn').classList.toggle('active', emojiOpen);
}
function hideEmoji() {
emojiOpen = false;
document.getElementById('chat-emoji-picker')?.classList.remove('show');
document.getElementById('chat-emoji-btn')?.classList.remove('active');
}
document.getElementById('chat-emoji-btn')?.addEventListener('click', toggleEmoji);
// Desktop'ta emoji butonu göster
if (IS_DESKTOP) {
const eb = document.getElementById('chat-emoji-btn');
if (eb) eb.style.display = '';
}
/* ── GIF picker ──────────────────────────── */
async function searchGifs(q) {
const grid = document.getElementById('chat-gif-grid');
if (!grid) return;
if (!TENOR_KEY) {
grid.innerHTML = '<div class="chat-gif-ph">Tenor API key ayarlanmamış.</div>';
return;
}
grid.innerHTML = '<div class="chat-gif-ph">Yükleniyor…</div>';
try {
const ep = q
? `https://tenor.googleapis.com/v2/search?q=${encodeURIComponent(q)}&key=${TENOR_KEY}&limit=12&media_filter=gif`
: `https://tenor.googleapis.com/v2/featured?key=${TENOR_KEY}&limit=12&media_filter=gif`;
const r = await fetch(ep);
const d = await r.json();
const results = d.results || [];
if (!results.length) { grid.innerHTML = '<div class="chat-gif-ph">Sonuç yok.</div>'; return; }
grid.innerHTML = '';
results.forEach(g => {
const url = g.media_formats?.tinygif?.url || g.media_formats?.gif?.url || '';
if (!url) return;
const item = document.createElement('div');
item.className = 'chat-gif-item';
item.innerHTML = `<img src="${escH(url)}" alt="" loading="lazy">`;
item.addEventListener('click', () => window.chatSendGif(url));
grid.appendChild(item);
});
} catch(_) { grid.innerHTML = '<div class="chat-gif-ph">Yüklenemedi.</div>'; }
}
function toggleGif() {
gifOpen = !gifOpen;
if (gifOpen) { hideEmoji(); if (TENOR_KEY) searchGifs(''); setTimeout(() => document.getElementById('chat-gif-input')?.focus(), 80); }
document.getElementById('chat-gif-picker').classList.toggle('show', gifOpen);
document.getElementById('chat-gif-btn').classList.toggle('active', gifOpen);
}
function hideGif() {
gifOpen = false;
document.getElementById('chat-gif-picker')?.classList.remove('show');
document.getElementById('chat-gif-btn')?.classList.remove('active');
}
document.getElementById('chat-gif-btn')?.addEventListener('click', toggleGif);
document.getElementById('chat-gif-input')?.addEventListener('input', function() {
clearTimeout(gifTimer);
gifTimer = setTimeout(() => searchGifs(this.value.trim()), 380);
});
/* ── Sesli arama ─────────────────────────── */
document.getElementById('call-btn')?.addEventListener('click', async function() {
const userId = this.dataset.userId;
if (!userId || !window.cpStartCall) { if (window.showToast) showToast('Arama özelliği yüklenemedi.','error'); return; }
window.cpConvUserId = userId;
window.cpStartCall();
});
/* ── Auto focus ──────────────────────────── */
if (IS_DESKTOP) inputEl.focus();
})();
</script>
@endpush
@@ -0,0 +1,82 @@
@extends('frontend.layouts.app')
@section('title', 'Bildirimler — Animexe')
@push('styles')
<style>
.notif-wrap{max-width:700px;margin:0 auto;padding:80px 20px 60px}
.notif-hero{margin-bottom:28px}
.notif-hero h1{font-size:1.5rem;font-weight:800;color:var(--text);display:flex;align-items:center;gap:10px}
.notif-hero p{color:var(--text3);font-size:.88rem;margin-top:4px}
.notif-card{
background:var(--surface);border:1px solid var(--border);border-radius:12px;
padding:16px 18px;margin-bottom:10px;display:flex;align-items:flex-start;gap:14px;
transition:border-color .15s,background .15s;
}
.notif-card:hover{border-color:rgba(0,245,255,.25);background:rgba(0,245,255,.02)}
.notif-card.unread{border-left:3px solid var(--accent2);background:rgba(0,245,255,.03)}
.notif-icon{
width:40px;height:40px;border-radius:50%;background:rgba(0,245,255,.1);border:1px solid rgba(0,245,255,.2);
display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:1.1rem;color:var(--accent2);
}
.notif-body{flex:1;min-width:0}
.notif-title{font-size:.88rem;font-weight:600;color:var(--text);margin-bottom:3px}
.notif-sub{font-size:.78rem;color:var(--text2);line-height:1.5}
.notif-time{font-size:.68rem;color:var(--text3);margin-top:5px}
.notif-empty{text-align:center;padding:60px 20px;color:var(--text3)}
.notif-empty i{font-size:2.5rem;display:block;margin-bottom:12px;opacity:.3}
</style>
@endpush
@section('content')
<div class="notif-wrap">
<div class="notif-hero">
<h1><i class="bi bi-bell-fill" style="color:var(--accent2)"></i> Bildirimler</h1>
<p>Takip ettiğin animelerin yeni bölüm bildirimleri burada görünür.</p>
</div>
@if($notifications->isEmpty())
<div class="notif-empty">
<i class="bi bi-bell-slash"></i>
<div>Henüz bildiriminiz yok.</div>
<div style="font-size:.8rem;margin-top:6px">Anime sayfalarından animeleri takip edin, yeni bölüm eklenince bilgilendirilirsiniz.</div>
</div>
@else
@foreach($notifications as $n)
@php
$d = $n->data;
$isEp = $n->type === 'episode';
$isAdmin = $n->type === 'admin';
$url = $isEp && isset($d['anime_slug'])
? route('watch', [$d['anime_slug'], $d['season_number'] ?? 1, $d['episode_number'] ?? 1])
: ($isAdmin && !empty($d['url']) ? $d['url'] : '#');
@endphp
<a href="{{ $url }}" style="text-decoration:none;display:block">
<div class="notif-card {{ !$n->is_read ? 'unread' : '' }}">
<div class="notif-icon" style="{{ $isAdmin ? 'background:rgba(255,45,125,.1);border-color:rgba(255,45,125,.25);color:var(--accent)' : '' }}">
<i class="bi {{ $isAdmin ? ($d['icon'] ?? 'bi-megaphone-fill') : ($isEp ? 'bi-play-circle-fill' : 'bi-bell-fill') }}"></i>
</div>
<div class="notif-body">
@if($isEp)
<div class="notif-title">{{ $d['anime_title'] ?? 'Anime' }} Yeni Bölüm!</div>
<div class="notif-sub">
Sezon {{ $d['season_number'] ?? 1 }}, {{ $d['episode_number'] ?? 1 }}. Bölüm eklendi
@if(!empty($d['episode_title'])) <em>{{ $d['episode_title'] }}</em>@endif
</div>
@else
<div class="notif-title">{{ $d['title'] ?? 'Bildirim' }}</div>
@if(!empty($d['body']))<div class="notif-sub">{{ $d['body'] }}</div>@endif
@endif
<div class="notif-time">{{ $n->created_at ? \Carbon\Carbon::parse($n->created_at)->diffForHumans() : '' }}</div>
</div>
@if(!$n->is_read)
<div style="width:8px;height:8px;border-radius:50%;background:var(--accent2);flex-shrink:0;margin-top:4px"></div>
@endif
</div>
</a>
@endforeach
<div class="mt-4">{{ $notifications->links() }}</div>
@endif
</div>
@endsection
@@ -0,0 +1,70 @@
@if ($paginator->hasPages())
<style>
.pag{display:flex;align-items:center;justify-content:center;gap:6px;flex-wrap:wrap;padding:8px 0}
.pag-btn{
display:inline-flex;align-items:center;justify-content:center;
min-width:40px;height:40px;padding:0 6px;border-radius:10px;
font-size:.85rem;font-weight:600;text-decoration:none;
border:1px solid rgba(0,245,255,.12);
background:rgba(17,17,31,.85);
color:var(--text2,#a0a8c0);
transition:background .15s,border-color .15s,color .15s,transform .12s;
cursor:pointer;
}
.pag-btn:hover{
background:rgba(0,245,255,.1);
border-color:rgba(0,245,255,.35);
color:#fff;
transform:translateY(-1px);
}
.pag-btn.active{
background:linear-gradient(135deg,var(--accent,#ff2d7d),var(--accent3,#b84dff));
border-color:transparent;
color:#fff;
font-weight:800;
box-shadow:0 0 18px rgba(255,45,125,.35);
cursor:default;
pointer-events:none;
}
.pag-btn.disabled{
opacity:.3;cursor:default;pointer-events:none;
}
.pag-dots{
display:inline-flex;align-items:center;justify-content:center;
min-width:40px;height:40px;color:var(--text3,#555);font-size:.9rem;
letter-spacing:.1em;
}
.pag-arrow{font-size:1rem}
</style>
<nav class="pag" aria-label="Sayfalama">
{{-- Önceki --}}
@if ($paginator->onFirstPage())
<span class="pag-btn disabled pag-arrow"></span>
@else
<a class="pag-btn pag-arrow" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="Önceki sayfa"></a>
@endif
{{-- Sayfa numaraları --}}
@foreach ($elements as $element)
@if (is_string($element))
<span class="pag-dots">···</span>
@endif
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<span class="pag-btn active" aria-current="page">{{ $page }}</span>
@else
<a class="pag-btn" href="{{ $url }}">{{ $page }}</a>
@endif
@endforeach
@endif
@endforeach
{{-- Sonraki --}}
@if ($paginator->hasMorePages())
<a class="pag-btn pag-arrow" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="Sonraki sayfa"></a>
@else
<span class="pag-btn disabled pag-arrow"></span>
@endif
</nav>
@endif
@@ -0,0 +1,49 @@
{{-- Sade banner reklam $ad: App\Models\Ad
NOT: class/attribute isimleri bilerek nötr ("ad"/"banner" içermez)
adblocker'lar .ad-banner / [data-ad-id] gibi seçicileri cosmetic filter ile gizliyor.
Ayrıca .home-section KULLANMA o class'ta opacity:0 (scroll reveal) var. --}}
@if($ad && $ad->media_url)
@once
<style>
.hp-feature-slot{
max-width:1400px;
margin:0 auto;
padding:32px clamp(16px,4vw,48px) 40px;
position:relative;
}
.hp-feature-inner{max-width:970px;margin:0 auto}
.hp-feature-label{
font-size:10px;letter-spacing:.14em;text-transform:uppercase;
color:rgba(255,255,255,.28);margin-bottom:7px;text-align:center;
}
.hp-feature-media{
display:block;border-radius:12px;overflow:hidden;
border:1px solid rgba(255,255,255,.06);
background:rgba(255,255,255,.02);
line-height:0;
}
.hp-feature-media img{width:100%;display:block;max-height:140px;object-fit:cover}
@media(max-width:640px){
.hp-feature-slot{padding:24px 16px 28px}
.hp-feature-media img{max-height:96px}
}
</style>
@endonce
<div class="hp-feature-slot">
<div class="hp-feature-inner">
<div class="hp-feature-label">Sponsorlu</div>
@if($ad->click_url)
<a href="{{ $ad->click_url }}" target="_blank" rel="noopener sponsored"
class="hp-feature-media" data-slot-id="{{ $ad->id }}">
<img src="{{ $ad->media_url }}" alt="{{ $ad->name }}" loading="lazy">
</a>
@else
<div class="hp-feature-media" data-slot-id="{{ $ad->id }}">
<img src="{{ $ad->media_url }}" alt="{{ $ad->name }}" loading="lazy">
</div>
@endif
</div>
</div>
@endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,129 @@
@extends('frontend.layouts.app')
@section('title', 'Aktivasyon Kodu Gir — Animexe Premium')
@push('styles')
<style>
#pg{background:#06060d;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:80px 20px 60px}
.act-card{
width:100%;max-width:480px;
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.09);
border-radius:24px;padding:44px 40px 40px;position:relative;overflow:hidden;
}
.act-card::before{
content:'';position:absolute;top:-80px;left:50%;transform:translateX(-50%);
width:280px;height:200px;border-radius:50%;
background:radial-gradient(rgba(0,245,255,.1),transparent 70%);
pointer-events:none;
}
.act-icon{
width:64px;height:64px;border-radius:18px;margin:0 auto 20px;
background:linear-gradient(135deg,rgba(0,245,255,.15),rgba(184,77,255,.12));
border:1px solid rgba(0,245,255,.2);
display:flex;align-items:center;justify-content:center;font-size:1.8rem;color:#00f5ff;
position:relative;z-index:1;
}
.act-title{font-size:1.4rem;font-weight:900;color:#fff;text-align:center;margin-bottom:6px;position:relative;z-index:1}
.act-sub{font-size:.85rem;color:rgba(255,255,255,.4);text-align:center;margin-bottom:32px;line-height:1.6;position:relative;z-index:1}
.act-label{font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,.4);margin-bottom:8px}
.act-input{
width:100%;background:rgba(255,255,255,.06);border:1.5px solid rgba(0,245,255,.2);
border-radius:14px;padding:16px 20px;color:#fff;font-size:1.1rem;font-weight:800;
letter-spacing:.18em;text-align:center;text-transform:uppercase;outline:none;
font-family:inherit;transition:border-color .2s,box-shadow .2s;box-sizing:border-box;
}
.act-input::placeholder{letter-spacing:.05em;font-weight:400;text-transform:none;font-size:.9rem;color:rgba(255,255,255,.25)}
.act-input:focus{border-color:#00f5ff;box-shadow:0 0 0 4px rgba(0,245,255,.1)}
.act-input.is-invalid{border-color:#ff2d7d!important;box-shadow:0 0 0 4px rgba(255,45,125,.1)!important}
.act-error{
display:flex;align-items:center;gap:7px;
padding:11px 14px;border-radius:10px;margin-top:10px;
background:rgba(255,45,125,.08);border:1px solid rgba(255,45,125,.2);
font-size:.82rem;color:#ff2d7d;font-weight:600;
}
.act-btn{
width:100%;padding:15px;border-radius:14px;border:none;cursor:pointer;font-family:inherit;
font-size:.95rem;font-weight:800;letter-spacing:.03em;margin-top:18px;
background:linear-gradient(135deg,#00f5ff 0%,#0080ff 60%,#b84dff 100%);
color:#06060d;box-shadow:0 0 30px rgba(0,245,255,.2),0 6px 20px rgba(0,0,0,.4);
transition:transform .15s,box-shadow .15s;
}
.act-btn:hover{transform:translateY(-2px);box-shadow:0 0 40px rgba(0,245,255,.35),0 8px 28px rgba(0,0,0,.5)}
.act-divider{display:flex;align-items:center;gap:12px;margin:22px 0;color:rgba(255,255,255,.18);font-size:.75rem}
.act-divider::before,.act-divider::after{content:'';flex:1;height:1px;background:rgba(255,255,255,.08)}
.act-back{
display:flex;align-items:center;justify-content:center;gap:7px;
padding:12px;border-radius:12px;border:1px solid rgba(255,255,255,.1);
background:rgba(255,255,255,.04);color:rgba(255,255,255,.5);
font-size:.84rem;font-weight:700;text-decoration:none;transition:all .15s;
}
.act-back:hover{border-color:rgba(255,255,255,.2);color:#fff;background:rgba(255,255,255,.08)}
.act-hint{
margin-top:20px;padding:12px 16px;border-radius:10px;
background:rgba(0,245,255,.04);border:1px solid rgba(0,245,255,.1);
font-size:.76rem;color:rgba(255,255,255,.35);line-height:1.55;text-align:center;
}
.act-hint strong{color:rgba(0,245,255,.7)}
</style>
@endpush
@section('content')
<div id="pg">
<div class="act-card">
<div class="act-icon"><i class="bi bi-key-fill"></i></div>
<h1 class="act-title">Aktivasyon Kodu Gir</h1>
<p class="act-sub">Satın alma sonrası aldığın kodu girerek Premium üyeliğini hemen aktifleştir.</p>
@guest
<div style="text-align:center;padding:8px 0 16px">
<a href="{{ route('frontend.login') }}?next={{ urlencode(route('premium.activate')) }}"
style="display:inline-flex;align-items:center;gap:8px;padding:13px 28px;border-radius:12px;background:linear-gradient(135deg,#00f5ff,#b84dff);color:#06060d;font-weight:800;font-size:.9rem;text-decoration:none">
<i class="bi bi-box-arrow-in-right"></i> Giriş Yap
</a>
<div style="margin-top:12px;font-size:.78rem;color:rgba(255,255,255,.3)">
Hesabın yoksa <a href="{{ route('frontend.register') }}" style="color:#00f5ff;font-weight:700">kayıt ol</a>
</div>
</div>
@else
<form method="POST" action="{{ route('premium.activate.redeem') }}">
@csrf
<div class="act-label">Aktivasyon Kodu</div>
<input type="text" name="code" id="act-inp"
class="act-input {{ $errors->has('code') ? 'is-invalid' : '' }}"
placeholder="XXXX-XXXX-XXXX"
value="{{ old('code') }}"
maxlength="32" autocomplete="off" autocapitalize="characters" spellcheck="false"
autofocus>
@if($errors->has('code'))
<div class="act-error"><i class="bi bi-exclamation-circle-fill"></i> {{ $errors->first('code') }}</div>
@endif
<button type="submit" class="act-btn">
<i class="bi bi-lightning-charge-fill me-1"></i> Aktifleştir
</button>
</form>
<div class="act-hint">
Kod formatı: <strong>XXXX-XXXX-XXXX</strong> (büyük harf + tire)<br>
Kodu doğru girdiğinden emin ol. Büyük/küçük harf fark etmez.
</div>
@endguest
<div class="act-divider">veya</div>
<a href="{{ route('premium.plans') }}" class="act-back">
<i class="bi bi-arrow-left"></i> Premium Planlarına Dön
</a>
</div>
</div>
@auth
<script>
(function(){
const inp = document.getElementById('act-inp');
if(!inp) return;
inp.addEventListener('input', function(){
let v = this.value.replace(/[^A-Za-z0-9]/g,'').toUpperCase();
const parts = v.match(/.{1,4}/g) || [];
this.value = parts.join('-').slice(0, 14);
});
})();
</script>
@endauth
@endsection
@@ -0,0 +1,662 @@
@extends('frontend.layouts.app')
@section('title', 'Animexe Premium — Anime Deneyimini Zirveye Taşı')
@section('meta_description', 'Animexe Premium ile reklamsız izle, özel kozmotikler kazan, erken erişim fırsatlarından yararlan.')
@push('styles')
<style>
:root{--prm-cyan:#00f5ff;--prm-pink:#ff2d7d;--prm-purple:#b84dff;--prm-gold:#ffd60a;--prm-bg:#06060d}
/* ── Genel ── */
#pg{background:var(--prm-bg);min-height:100vh}
.prm-wrap{max-width:1160px;margin:0 auto;padding:0 20px 80px}
/* ── Hero ── */
.prm-hero{
position:relative;text-align:center;
padding:100px 20px 80px;overflow:hidden;
}
.prm-hero-bg{
position:absolute;inset:0;z-index:0;
background:radial-gradient(ellipse 80% 60% at 50% 0%,rgba(0,245,255,.09) 0%,transparent 70%),
radial-gradient(ellipse 50% 40% at 80% 30%,rgba(184,77,255,.07) 0%,transparent 60%),
radial-gradient(ellipse 40% 30% at 20% 60%,rgba(255,45,125,.06) 0%,transparent 60%);
}
/* Yıldız parçacıkları */
.prm-stars{position:absolute;inset:0;z-index:0;pointer-events:none}
.prm-stars span{
position:absolute;width:2px;height:2px;border-radius:50%;
background:#fff;animation:twinkle var(--d,3s) ease-in-out infinite var(--delay,0s);
opacity:0;
}
@keyframes twinkle{0%,100%{opacity:0;transform:scale(1)}50%{opacity:.7;transform:scale(1.4)}}
.prm-hero-inner{position:relative;z-index:1}
.prm-hero-badge{
display:inline-flex;align-items:center;gap:7px;
padding:6px 16px;border-radius:30px;
background:rgba(0,245,255,.08);border:1px solid rgba(0,245,255,.25);
font-size:.75rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;
color:var(--prm-cyan);margin-bottom:24px;
}
.prm-hero-badge i{font-size:.85rem}
.prm-hero-title{
font-size:clamp(2.2rem,5vw,3.8rem);font-weight:900;line-height:1.15;
margin-bottom:16px;letter-spacing:-.02em;
}
.prm-hero-title .grad{
background:linear-gradient(120deg,var(--prm-cyan) 0%,var(--prm-purple) 50%,var(--prm-pink) 100%);
-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;
}
.prm-hero-sub{font-size:1.08rem;color:rgba(255,255,255,.5);max-width:560px;margin:0 auto 40px;line-height:1.65}
.prm-hero-stats{display:flex;gap:32px;justify-content:center;flex-wrap:wrap;margin-bottom:8px}
.prm-stat{display:flex;flex-direction:column;align-items:center;gap:2px}
.prm-stat-val{font-size:1.5rem;font-weight:900;color:#fff;line-height:1}
.prm-stat-lbl{font-size:.7rem;color:rgba(255,255,255,.35);text-transform:uppercase;letter-spacing:.08em}
/* ── Aktivasyon başarı banner ── */
.prm-success-banner{
display:flex;align-items:center;gap:14px;
padding:16px 22px;border-radius:14px;margin-bottom:32px;
background:rgba(50,220,100,.08);border:1px solid rgba(50,220,100,.3);
animation:slideDown .4s ease;
}
@keyframes slideDown{from{opacity:0;transform:translateY(-12px)}to{opacity:1;transform:none}}
.prm-success-banner i{font-size:1.6rem;color:#32dc64;flex-shrink:0}
.prm-success-banner strong{color:#32dc64;font-size:.95rem;display:block;margin-bottom:2px}
.prm-success-banner span{font-size:.8rem;color:rgba(255,255,255,.5)}
/* ── Planlar grid ── */
.prm-section-lbl{
text-align:center;margin-bottom:48px;
}
.prm-section-lbl h2{font-size:1.6rem;font-weight:800;color:#fff;margin-bottom:8px}
.prm-section-lbl p{font-size:.9rem;color:rgba(255,255,255,.4)}
.prm-plans{
display:grid;grid-template-columns:repeat(3,1fr);gap:18px;
margin-bottom:72px;
}
@media(max-width:900px){.prm-plans{grid-template-columns:1fr}}
.prm-card{
position:relative;border-radius:20px;padding:32px 26px 28px;
background:rgba(255,255,255,.03);
border:1px solid rgba(255,255,255,.08);
transition:transform .2s ease,border-color .2s ease,box-shadow .2s ease;
display:flex;flex-direction:column;
}
.prm-card:hover{transform:translateY(-4px);border-color:rgba(255,255,255,.15);box-shadow:0 20px 60px rgba(0,0,0,.4)}
.prm-card.featured{
background:linear-gradient(145deg,rgba(0,245,255,.06) 0%,rgba(184,77,255,.05) 60%,rgba(255,45,125,.05) 100%);
border-color:rgba(0,245,255,.3);
box-shadow:0 0 0 1px rgba(0,245,255,.1),0 20px 60px rgba(0,245,255,.08);
}
.prm-card.featured:hover{box-shadow:0 0 0 1px rgba(0,245,255,.2),0 28px 70px rgba(0,245,255,.14)}
/* Badge */
.prm-card-badge{
position:absolute;top:-13px;left:50%;transform:translateX(-50%);
padding:4px 18px;border-radius:20px;font-size:.7rem;font-weight:800;
letter-spacing:.08em;text-transform:uppercase;white-space:nowrap;
background:linear-gradient(90deg,var(--prm-cyan),var(--prm-purple));
color:#06060d;box-shadow:0 0 20px rgba(0,245,255,.3);
}
.prm-card-badge.gold{background:linear-gradient(90deg,#ffd60a,#ff9f0a);box-shadow:0 0 20px rgba(255,214,10,.3)}
.prm-card-badge.pink{background:linear-gradient(90deg,var(--prm-pink),var(--prm-purple));box-shadow:0 0 20px rgba(255,45,125,.3)}
/* Plan içerik */
.prm-plan-name{font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.12em;color:rgba(255,255,255,.4);margin-bottom:10px}
.prm-plan-price-wrap{margin-bottom:6px;line-height:1}
.prm-plan-price{font-size:2.8rem;font-weight:900;color:#fff;letter-spacing:-.04em}
.prm-plan-currency{font-size:1.2rem;font-weight:700;color:rgba(255,255,255,.6);vertical-align:top;margin-top:8px;display:inline-block}
.prm-plan-period{font-size:.8rem;color:rgba(255,255,255,.35);margin-top:4px}
.prm-plan-per-day{font-size:.72rem;color:rgba(255,255,255,.25);margin-bottom:4px}
.prm-plan-trial{
display:inline-flex;align-items:center;gap:5px;
padding:3px 10px;border-radius:10px;
background:rgba(255,214,10,.1);border:1px solid rgba(255,214,10,.25);
font-size:.7rem;font-weight:700;color:#ffd60a;margin-bottom:18px;
}
.prm-divider{height:1px;background:rgba(255,255,255,.07);margin:18px 0}
/* Feature list */
.prm-features{list-style:none;padding:0;margin:0 0 24px;display:flex;flex-direction:column;gap:9px;flex:1}
.prm-features li{display:flex;align-items:flex-start;gap:9px;font-size:.83rem;color:rgba(255,255,255,.65);line-height:1.4}
.prm-features li i.bi-check-circle-fill{color:var(--prm-cyan);font-size:.85rem;flex-shrink:0;margin-top:1px}
.prm-features li i.bi-x-circle{color:rgba(255,255,255,.15);font-size:.85rem;flex-shrink:0;margin-top:1px}
.prm-features li.dim{color:rgba(255,255,255,.2)}
/* CTA Buton */
.prm-btn-buy{
display:flex;align-items:center;justify-content:center;gap:8px;
width:100%;padding:13px;border-radius:12px;border:none;
font-size:.9rem;font-weight:800;cursor:pointer;font-family:inherit;
text-decoration:none;letter-spacing:.02em;
transition:transform .15s,box-shadow .15s,opacity .15s;
margin-top:auto;
}
.prm-btn-buy:hover{transform:translateY(-2px)}
.prm-btn-buy.primary{
background:linear-gradient(135deg,var(--prm-cyan) 0%,#0080ff 60%,var(--prm-purple) 100%);
color:#06060d;box-shadow:0 0 30px rgba(0,245,255,.25),0 6px 20px rgba(0,0,0,.4);
}
.prm-btn-buy.primary:hover{box-shadow:0 0 40px rgba(0,245,255,.4),0 8px 28px rgba(0,0,0,.5)}
.prm-btn-buy.outline{
background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.12);
color:rgba(255,255,255,.75);
}
.prm-btn-buy.outline:hover{background:rgba(255,255,255,.09);border-color:rgba(255,255,255,.25);color:#fff}
.prm-btn-buy.active-plan{
background:rgba(50,220,100,.08);border:1px solid rgba(50,220,100,.3);
color:#32dc64;cursor:default;
}
.prm-btn-buy.active-plan:hover{transform:none}
.prm-btn-buy.disabled{
background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);
color:rgba(255,255,255,.2);cursor:not-allowed;
}
.prm-btn-buy.disabled:hover{transform:none}
/* ── Nasıl Çalışır ── */
.prm-how{
display:grid;grid-template-columns:repeat(3,1fr);gap:2px;
margin-bottom:72px;background:rgba(255,255,255,.04);
border:1px solid rgba(255,255,255,.07);border-radius:20px;overflow:hidden;
}
@media(max-width:700px){.prm-how{grid-template-columns:1fr}}
.prm-how-step{
padding:32px 28px;position:relative;
background:rgba(6,6,13,.6);
}
.prm-how-step:not(:last-child)::after{
content:'';position:absolute;right:0;top:20%;bottom:20%;
width:1px;background:rgba(255,255,255,.07);
}
@media(max-width:700px){.prm-how-step:not(:last-child)::after{display:none}}
.prm-how-num{
width:40px;height:40px;border-radius:12px;display:flex;align-items:center;justify-content:center;
font-size:1.1rem;font-weight:900;margin-bottom:16px;
}
.prm-how-num.c1{background:rgba(0,245,255,.12);color:var(--prm-cyan)}
.prm-how-num.c2{background:rgba(184,77,255,.12);color:var(--prm-purple)}
.prm-how-num.c3{background:rgba(50,220,100,.12);color:#32dc64}
.prm-how-step h3{font-size:.95rem;font-weight:800;color:#fff;margin-bottom:8px}
.prm-how-step p{font-size:.8rem;color:rgba(255,255,255,.4);line-height:1.6;margin:0}
/* ── Aktivasyon Kutusu ── */
.prm-activate-box{
border-radius:20px;padding:44px 36px;margin-bottom:72px;
background:linear-gradient(135deg,rgba(0,245,255,.04),rgba(184,77,255,.03));
border:1px solid rgba(0,245,255,.15);
text-align:center;position:relative;overflow:hidden;
}
.prm-activate-box::before{
content:'';position:absolute;top:-60px;left:50%;transform:translateX(-50%);
width:300px;height:200px;border-radius:50%;
background:radial-gradient(rgba(0,245,255,.08),transparent 70%);
pointer-events:none;
}
.prm-activate-box h2{font-size:1.45rem;font-weight:800;color:#fff;margin-bottom:8px}
.prm-activate-box p{font-size:.88rem;color:rgba(255,255,255,.4);margin-bottom:28px;line-height:1.6}
.prm-code-form{display:flex;gap:10px;max-width:500px;margin:0 auto;flex-wrap:wrap;justify-content:center}
.prm-code-input{
flex:1;min-width:220px;
background:rgba(255,255,255,.06);border:1px solid rgba(0,245,255,.25);
border-radius:12px;padding:14px 18px;color:#fff;font-size:1rem;font-weight:700;
letter-spacing:.12em;text-align:center;text-transform:uppercase;outline:none;
font-family:inherit;transition:border-color .2s,box-shadow .2s;
}
.prm-code-input::placeholder{letter-spacing:.05em;font-weight:400;text-transform:none;font-size:.88rem;color:rgba(255,255,255,.3)}
.prm-code-input:focus{border-color:var(--prm-cyan);box-shadow:0 0 0 3px rgba(0,245,255,.12)}
.prm-code-input.is-invalid{border-color:var(--prm-pink)!important;box-shadow:0 0 0 3px rgba(255,45,125,.1)!important}
.prm-code-btn{
padding:14px 28px;border-radius:12px;border:none;cursor:pointer;font-family:inherit;
font-size:.9rem;font-weight:800;letter-spacing:.03em;
background:linear-gradient(135deg,var(--prm-cyan),var(--prm-purple));
color:#06060d;box-shadow:0 0 24px rgba(0,245,255,.2);
transition:transform .15s,box-shadow .15s;
}
.prm-code-btn:hover{transform:translateY(-2px);box-shadow:0 0 36px rgba(0,245,255,.35)}
.prm-code-error{
width:100%;text-align:center;font-size:.8rem;color:var(--prm-pink);
margin-top:8px;font-weight:600;
}
.prm-activate-login-note{
margin-top:14px;font-size:.78rem;color:rgba(255,255,255,.3);
}
.prm-activate-login-note a{color:var(--prm-cyan);text-decoration:none;font-weight:700}
.prm-activate-login-note a:hover{text-decoration:underline}
/* ── Özellik Showcase ── */
.prm-features-grid{
display:grid;grid-template-columns:repeat(2,1fr);gap:32px;margin-bottom:72px;
}
@media(max-width:700px){.prm-features-grid{grid-template-columns:1fr}}
.prm-feat-cat{
background:rgba(255,255,255,.025);border:1px solid rgba(255,255,255,.07);
border-radius:16px;padding:24px;
}
.prm-feat-cat-hdr{
display:flex;align-items:center;gap:10px;margin-bottom:18px;padding-bottom:14px;
border-bottom:1px solid rgba(255,255,255,.06);
}
.prm-feat-cat-icon{
width:36px;height:36px;border-radius:10px;display:flex;align-items:center;justify-content:center;
font-size:1rem;flex-shrink:0;
}
.prm-feat-cat-hdr h3{font-size:.9rem;font-weight:800;color:#fff;margin:0}
.prm-feat-list{display:flex;flex-direction:column;gap:10px}
.prm-feat-item{display:flex;align-items:flex-start;gap:10px}
.prm-feat-item-icon{
width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;
font-size:.75rem;flex-shrink:0;margin-top:1px;
}
.prm-feat-item-body{flex:1;min-width:0}
.prm-feat-item-name{font-size:.82rem;font-weight:700;color:rgba(255,255,255,.8);margin-bottom:2px}
.prm-feat-item-desc{font-size:.72rem;color:rgba(255,255,255,.35);line-height:1.45}
/* Kozmetik Preview */
.prm-cosmetics{
margin-bottom:72px;text-align:center;
}
.prm-cosmetics-title{font-size:1.35rem;font-weight:800;color:#fff;margin-bottom:8px}
.prm-cosmetics-sub{font-size:.85rem;color:rgba(255,255,255,.35);margin-bottom:36px}
.prm-cosmetics-grid{display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:28px}
/* Username color chips */
.uc-chip{
padding:5px 16px;border-radius:20px;font-size:.82rem;font-weight:700;
background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);
cursor:default;white-space:nowrap;
}
/* Frame badges */
.frame-badge{
width:52px;height:52px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;
font-size:1.2rem;font-weight:800;color:#fff;position:relative;
}
.frame-badge::before{
content:'';position:absolute;inset:-3px;border-radius:50%;
animation:frame-spin 3s linear infinite;
}
@keyframes frame-spin{to{transform:rotate(360deg)}}
.frame-badge.fr-neon::before{background:conic-gradient(#00f5ff,#b84dff,#00f5ff)}
.frame-badge.fr-fire::before{background:conic-gradient(#ff6b35,#ff2d7d,#ff8c00,#ff6b35)}
.frame-badge.fr-galaxy::before{background:conic-gradient(#7c3aed,#b84dff,#4a1d96,#7c3aed)}
.frame-badge.fr-gold::before{background:conic-gradient(#ffd700,#ff8c00,#ffd700)}
.frame-badge.fr-sakura::before{background:conic-gradient(#ff9ec4,#ff2d7d,#ffd6e7,#ff9ec4)}
.frame-badge.fr-rainbow::before{background:conic-gradient(#f00,#ff8c00,#ffd700,#0c0,#08f,#8b00ff,#f08,#f00)}
.frame-badge.fr-ice::before{background:conic-gradient(#a8edff,#60cfff,#a8edff)}
.frame-badge-inner{position:relative;z-index:1;width:44px;height:44px;border-radius:50%;background:#111;display:flex;align-items:center;justify-content:center;font-size:.65rem;text-align:center;padding:2px;color:rgba(255,255,255,.7)}
/* Comment bg preview */
.cb-preview{
padding:10px 16px;border-radius:10px;font-size:.8rem;color:#fff;font-weight:600;
white-space:nowrap;position:relative;overflow:hidden;
}
.cb-preview::before{content:'';position:absolute;inset:0;opacity:.7}
.cb-fire::before{background:linear-gradient(135deg,#ff6b35,#ff2d7d)}
.cb-aurora::before{background:linear-gradient(135deg,#00c8d4,#b84dff)}
.cb-stars::before{background:linear-gradient(135deg,#1a1a3a,#4a1d96)}
.cb-sakura::before{background:linear-gradient(135deg,#ff9ec4,#ff2d7d)}
.cb-neon::before{background:linear-gradient(135deg,#00f5ff,#0040ff)}
.cb-galaxy::before{background:linear-gradient(135deg,#0a0020,#7c3aed)}
.cb-ice::before{background:linear-gradient(135deg,#a8edff,#006fbf)}
.cb-preview span{position:relative;z-index:1}
/* ── FAQ ── */
.prm-faq{margin-bottom:48px}
.prm-faq h2{font-size:1.35rem;font-weight:800;color:#fff;margin-bottom:28px;text-align:center}
.prm-faq-item{
border-bottom:1px solid rgba(255,255,255,.06);overflow:hidden;
}
.prm-faq-q{
display:flex;align-items:center;justify-content:space-between;
padding:16px 4px;cursor:pointer;font-size:.9rem;font-weight:700;color:rgba(255,255,255,.75);
transition:color .15s;user-select:none;
background:none;border:none;width:100%;text-align:left;font-family:inherit;
gap:12px;
}
.prm-faq-q:hover{color:#fff}
.prm-faq-q i{flex-shrink:0;transition:transform .25s ease;color:rgba(255,255,255,.3)}
.prm-faq-item.open .prm-faq-q i{transform:rotate(45deg);color:var(--prm-cyan)}
.prm-faq-a{
max-height:0;overflow:hidden;
transition:max-height .3s ease,padding .3s ease;
font-size:.84rem;color:rgba(255,255,255,.45);line-height:1.7;padding:0 4px;
}
.prm-faq-item.open .prm-faq-a{max-height:200px;padding-bottom:16px}
/* ── Mevcut Plan Banner ── */
.prm-active-banner{
display:flex;align-items:center;gap:16px;padding:18px 24px;border-radius:14px;
background:rgba(0,245,255,.06);border:1px solid rgba(0,245,255,.2);margin-bottom:36px;
flex-wrap:wrap;
}
.prm-active-banner i{font-size:1.8rem;color:var(--prm-cyan);flex-shrink:0}
.prm-active-banner-body{flex:1;min-width:0}
.prm-active-banner-title{font-size:.95rem;font-weight:800;color:#fff;margin-bottom:2px}
.prm-active-banner-sub{font-size:.78rem;color:rgba(255,255,255,.4)}
.prm-active-banner-action{flex-shrink:0}
.prm-active-banner-action a{
padding:9px 20px;border-radius:10px;border:1px solid rgba(0,245,255,.3);
background:rgba(0,245,255,.07);color:var(--prm-cyan);font-size:.8rem;font-weight:700;
text-decoration:none;transition:all .15s;white-space:nowrap;display:inline-block;
}
.prm-active-banner-action a:hover{background:rgba(0,245,255,.14);border-color:var(--prm-cyan)}
@media(max-width:600px){
.prm-hero{padding:72px 16px 56px}
.prm-activate-box{padding:32px 20px}
.prm-how-step{padding:24px 20px}
.prm-card{padding:26px 20px 22px}
}
</style>
@endpush
@section('content')
<div id="pg">
<div class="prm-hero">
<div class="prm-hero-bg"></div>
<div class="prm-stars" id="prm-stars"></div>
<div class="prm-hero-inner">
<div class="prm-hero-badge"><i class="bi bi-stars"></i> Animexe Premium</div>
<h1 class="prm-hero-title">
Anime deneyimini<br><span class="grad">zirveye taşı</span>
</h1>
<p class="prm-hero-sub">
Reklamsız izleme, özel profil kozmetikleri, erken bölüm erişimi ve daha fazlası hepsi tek bir Premium üyelikte.
</p>
<div class="prm-hero-stats">
<div class="prm-stat"><span class="prm-stat-val"></span><span class="prm-stat-lbl">İçerik</span></div>
<div class="prm-stat"><span class="prm-stat-val">{{ count($featureGroups ?? []) > 0 ? array_sum(array_map(fn($g) => count($g['perks']), $featureGroups)) : '20+' }}</span><span class="prm-stat-lbl">Özel Özellik</span></div>
<div class="prm-stat"><span class="prm-stat-val">7/24</span><span class="prm-stat-lbl">Destek</span></div>
</div>
</div>
</div>
<div class="prm-wrap">
{{-- Aktivasyon başarı mesajı --}}
@if(session('activation_success'))
<div class="prm-success-banner">
<i class="bi bi-patch-check-fill"></i>
<div>
<strong>Premium aktif! 🎉 {{ session('activation_success.plan') }} planına hoş geldin.</strong>
<span>Üyeliğin {{ session('activation_success.expires_at') }} tarihine kadar geçerli.</span>
</div>
</div>
@endif
{{-- Mevcut premium kullanıcı banner --}}
@auth
@if(auth()->user()->isPremium())
<div class="prm-active-banner">
<i class="bi bi-shield-fill-check"></i>
<div class="prm-active-banner-body">
<div class="prm-active-banner-title">Premium üyeliğin aktif 🎉</div>
<div class="prm-active-banner-sub">
Bitiş tarihi: {{ auth()->user()->premium_expires_at?->format('d MMMM Y') ?? auth()->user()->premium_expires_at?->format('d.m.Y') }}
&nbsp;·&nbsp; Süre uzatmak için aşağıdan yeni bir plan satın alabilirsin.
</div>
</div>
<div class="prm-active-banner-action">
<a href="{{ route('profile') }}"><i class="bi bi-person-circle me-1"></i>Profili Gör</a>
</div>
</div>
@endif
@endauth
{{-- Planlar --}}
@if(count($plans ?? []) > 0)
<div class="prm-section-lbl">
<h2>Planını Seç</h2>
<p>Tüm planlar aynı özellikleri içerir süre ne kadar uzunsa o kadar tasarruf edersin.</p>
</div>
<div class="prm-plans">
@foreach($plans as $idx => $plan)
@php
$isFeatured = $plan->badge_label && $idx == 1 || ($plan->badge_label && str_contains(strtolower($plan->badge_label), 'popül'));
$isActive = auth()->check() && auth()->user()->subscriptions()->where('plan_id', $plan->id)->where('status','active')->exists();
$perDay = $plan->duration_days > 0 ? round($plan->price / $plan->duration_days, 2) : 0;
$accentCls = $isFeatured ? 'featured' : '';
@endphp
<div class="prm-card {{ $accentCls }}">
@if($plan->badge_label)
@php
$badgeCls = match(true) {
str_contains(strtolower($plan->badge_label), 'değer') => 'gold',
str_contains(strtolower($plan->badge_label), 'popül') => '',
default => 'pink',
};
@endphp
<div class="prm-card-badge {{ $badgeCls }}">{{ $plan->badge_label }}</div>
@endif
<div class="prm-plan-name">{{ $plan->name }}</div>
<div class="prm-plan-price-wrap">
<span class="prm-plan-currency"></span><span class="prm-plan-price">{{ number_format($plan->price, 0, ',', '.') }}</span>
</div>
<div class="prm-plan-period">
@if($plan->duration_days >= 365)
{{ intdiv($plan->duration_days, 365) }} yıl
@elseif($plan->duration_days >= 30)
{{ intdiv($plan->duration_days, 30) }} ay
@else
{{ $plan->duration_days }} gün
@endif
&nbsp;·&nbsp; Günde {{ $perDay }}
</div>
@if($plan->trial_days)
<div class="prm-plan-trial"><i class="bi bi-gift-fill"></i> İlk {{ $plan->trial_days }} gün ücretsiz!</div>
@endif
<div class="prm-divider"></div>
@if(count($plan->features ?? []) > 0)
<ul class="prm-features">
@foreach($plan->features as $feat)
<li><i class="bi bi-check-circle-fill"></i> {{ $feat }}</li>
@endforeach
</ul>
@endif
@if($isActive)
<a class="prm-btn-buy active-plan"><i class="bi bi-check-circle-fill"></i> Mevcut Planın</a>
@elseif($plan->purchase_link)
<a href="{{ $plan->purchase_link }}" target="_blank" rel="noopener" class="prm-btn-buy {{ $isFeatured ? 'primary' : 'outline' }}">
<i class="bi bi-bag-fill"></i> Satın Al
</a>
@else
<span class="prm-btn-buy disabled"><i class="bi bi-clock"></i> Yakında</span>
@endif
</div>
@endforeach
</div>
@endif
{{-- Nasıl Çalışır --}}
<div class="prm-section-lbl">
<h2>Nasıl Çalışır?</h2>
<p>Üç adımda premium üyeliğini aktifleştir.</p>
</div>
<div class="prm-how">
<div class="prm-how-step">
<div class="prm-how-num c1"><i class="bi bi-1-circle-fill"></i></div>
<h3>Paket Seç & Öde</h3>
<p>Yukarıdaki planlardan birini seç, "Satın Al" butonuna tıkla ve güvenli ödeme sayfasına yönlendir. Ödemeyi tamamla.</p>
</div>
<div class="prm-how-step">
<div class="prm-how-num c2"><i class="bi bi-2-circle-fill"></i></div>
<h3>Aktivasyon Kodunu Al</h3>
<p>Ödemen onaylandıktan sonra sana özel bir aktivasyon kodu gönderilir. Bu kodu saklaman yeterli.</p>
</div>
<div class="prm-how-step">
<div class="prm-how-num c3"><i class="bi bi-3-circle-fill"></i></div>
<h3>Kodu Gir, Keyfini Çıkar</h3>
<p>Aşağıdaki kutuya aktivasyon kodunu gir. Anında aktif olur, tüm premium ayrıcalıklar hesabına yüklenir!</p>
</div>
</div>
{{-- Aktivasyon Kutusu --}}
<div class="prm-activate-box" id="activate">
<h2>Aktivasyon Kodun Var mı?</h2>
<p>Satın alma sonrası aldığın kodu girerek Premium üyeliğini hemen aktifleştir.</p>
@auth
<form method="POST" action="{{ route('premium.activate.redeem') }}" class="prm-code-form">
@csrf
<input type="text" name="code" id="prm-code-inp" class="prm-code-input {{ $errors->has('code') ? 'is-invalid' : '' }}"
placeholder="XXXX-XXXX-XXXX" maxlength="32" autocomplete="off" autocapitalize="characters"
value="{{ old('code') }}" spellcheck="false">
<button type="submit" class="prm-code-btn"><i class="bi bi-lightning-fill me-1"></i>Aktifleştir</button>
@if($errors->has('code'))
<div class="prm-code-error"><i class="bi bi-exclamation-circle me-1"></i>{{ $errors->first('code') }}</div>
@endif
</form>
@else
<form class="prm-code-form">
<input type="text" class="prm-code-input" placeholder="XXXX-XXXX-XXXX" disabled>
<button type="button" onclick="window.location.href='{{ route('frontend.register') }}?next={{ urlencode(route('premium.activate')) }}'" class="prm-code-btn">
<i class="bi bi-person-plus-fill me-1"></i>Giriş Yap & Aktifleştir
</button>
</form>
<div class="prm-activate-login-note">
Aktivasyon kodu kullanmak için <a href="{{ route('frontend.register') }}">üye olman</a> veya <a href="{{ route('frontend.login') }}">giriş yapman</a> gerekiyor.
</div>
@endauth
</div>
{{-- Özellikler Showcase --}}
@if(count($featureGroups ?? []) > 0)
<div class="prm-section-lbl">
<h2>Premium Ayrıcalıklar</h2>
<p>Premium üyeliğinle birlikte gelen tüm özellikler.</p>
</div>
<div class="prm-features-grid">
@foreach($featureGroups as $group)
@php
$colors = [
'Profil' => ['bg'=>'rgba(184,77,255,.12)','color'=>'#b84dff','icon'=>'bi-person-circle'],
'İzleme' => ['bg'=>'rgba(0,245,255,.12)','color'=>'#00f5ff','icon'=>'bi-play-circle-fill'],
'Sosyal' => ['bg'=>'rgba(255,45,125,.12)','color'=>'#ff2d7d','icon'=>'bi-chat-heart-fill'],
'Hesap' => ['bg'=>'rgba(255,214,10,.12)','color'=>'#ffd60a','icon'=>'bi-shield-fill-check'],
];
$c = $colors[$group['label']] ?? ['bg'=>'rgba(255,255,255,.08)','color'=>'#fff','icon'=>'bi-star-fill'];
@endphp
<div class="prm-feat-cat">
<div class="prm-feat-cat-hdr">
<div class="prm-feat-cat-icon" style="background:{{ $c['bg'] }};color:{{ $c['color'] }}">
<i class="bi {{ $c['icon'] }}"></i>
</div>
<h3>{{ $group['label'] }}</h3>
</div>
<div class="prm-feat-list">
@foreach(array_slice($group['perks'], 0, 6) as $perk)
<div class="prm-feat-item">
<div class="prm-feat-item-icon" style="background:{{ $c['bg'] }};color:{{ $c['color'] }}">
<i class="bi {{ $perk['icon'] ?? 'bi-check' }}"></i>
</div>
<div class="prm-feat-item-body">
<div class="prm-feat-item-name">{{ $perk['name'] }}</div>
@if(!empty($perk['desc']))<div class="prm-feat-item-desc">{{ $perk['desc'] }}</div>@endif
</div>
</div>
@endforeach
</div>
</div>
@endforeach
</div>
@endif
{{-- Kozmetik Preview --}}
<div class="prm-cosmetics">
<div class="prm-cosmetics-title">Profilini Kişiselleştir</div>
<div class="prm-cosmetics-sub">Premium üyeler için özel görsel özelleştirmeler</div>
<div style="display:flex;flex-wrap:wrap;gap:32px;justify-content:center;margin-bottom:36px">
<div style="text-align:center">
<div style="font-size:.7rem;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,.3);margin-bottom:14px">Profil Çerçeveleri</div>
<div style="display:flex;gap:14px;flex-wrap:wrap;justify-content:center">
@foreach(['neon','fire','galaxy','gold','sakura','rainbow','ice'] as $fr)
<div class="frame-badge fr-{{ $fr }}">
<div class="frame-badge-inner">{{ Str::upper(substr($fr,0,2)) }}</div>
</div>
@endforeach
</div>
</div>
<div style="text-align:center">
<div style="font-size:.7rem;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,.3);margin-bottom:14px">Yorum Arka Planları</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;justify-content:center">
@foreach(['fire'=>'Ateş','aurora'=>'Aurora','stars'=>'Yıldız','sakura'=>'Sakura','neon'=>'Neon','galaxy'=>'Galaksi','ice'=>'Buz'] as $bg => $lbl)
<div class="cb-preview cb-{{ $bg }}"><span>{{ $lbl }}</span></div>
@endforeach
</div>
</div>
</div>
<div style="text-align:center">
<div style="font-size:.7rem;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,.3);margin-bottom:14px">Kullanıcı Adı Renkleri</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;justify-content:center">
@foreach([
['gradient'=>'linear-gradient(90deg,#00f5ff,#0080ff)','name'=>'Okyanus'],
['gradient'=>'linear-gradient(90deg,#ff2d7d,#b84dff)','name'=>'Galaksi'],
['gradient'=>'linear-gradient(90deg,#ffd700,#ff8c00)','name'=>'Altın'],
['gradient'=>'linear-gradient(90deg,#32dc64,#00c8d4)','name'=>'Orman'],
['gradient'=>'linear-gradient(90deg,#ff6b35,#ff2d7d)','name'=>'Ateş'],
['gradient'=>'linear-gradient(90deg,#a8edff,#006fbf)','name'=>'Buz'],
['gradient'=>'linear-gradient(90deg,#b84dff,#7c3aed)','name'=>'Mor'],
] as $uc)
<span class="uc-chip" style="background:{{ $uc['gradient'] }};-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;border-color:rgba(255,255,255,.12)">{{ $uc['name'] }}</span>
@endforeach
</div>
</div>
</div>
{{-- FAQ --}}
<div class="prm-faq">
<h2>Sık Sorulan Sorular</h2>
@foreach([
['s'=>'Ödeme yaptıktan sonra nasıl aktifleştiririm?','c'=>'Ödemen onaylandıktan sonra sana aktivasyon kodu gönderilir. Bu sayfadaki "Aktivasyon Kodun Var mı?" kısmına kodu girip "Aktifleştir" butonuna tıklaman yeterli.'],
['s'=>'Mevcut Premium üyeliğim varken başka bir kod girebilir miyim?','c'=>'Evet! Mevcut üyelik süren bitmeden kod girersen süreler üst üste eklenir (stack). Hiç süre kaybetmezsin.'],
['s'=>'Aktivasyon kodum çalışmıyorsa ne yapmalıyım?','c'=>'Kodu büyük harflerle ve tire işaretleriyle birlikte girdiğinden emin ol (XXXX-XXXX-XXXX). Sorun devam ederse destek hattına başvur.'],
['s'=>'Premium üyeliğimi iptal edebilir miyim?','c'=>'Premium üyeliğin süre tabanlıdır; otomatik yenileme yoktur. Süre dolunca normal hesabına dönersin.'],
['s'=>'Premium özellikler hangi cihazlarda çalışır?','c'=>'Tüm cihazlarda (bilgisayar, tablet, telefon) çalışır. Hesabına giriş yaptığın her yerde premium ayrıcalıklarını kullanabilirsin.'],
] as $faq)
<div class="prm-faq-item">
<button class="prm-faq-q" onclick="this.closest('.prm-faq-item').classList.toggle('open')">
<span>{{ $faq['s'] }}</span>
<i class="bi bi-plus-lg"></i>
</button>
<div class="prm-faq-a">{{ $faq['c'] }}</div>
</div>
@endforeach
</div>
</div>{{-- /prm-wrap --}}
</div>{{-- /pg --}}
<script>
// Yıldız parçacıkları
(function(){
const c = document.getElementById('prm-stars');
if(!c) return;
for(let i=0;i<60;i++){
const s = document.createElement('span');
s.style.cssText = `left:${Math.random()*100}%;top:${Math.random()*100}%;--d:${2+Math.random()*4}s;--delay:-${Math.random()*4}s`;
c.appendChild(s);
}
})();
// Kod input: otomatik büyük harf + tire formatı
(function(){
const inp = document.getElementById('prm-code-inp');
if(!inp) return;
inp.addEventListener('input', function(){
let v = this.value.replace(/[^A-Za-z0-9]/g,'').toUpperCase();
const parts = v.match(/.{1,4}/g) || [];
this.value = parts.join('-').slice(0, 14);
});
})();
</script>
@endsection
@@ -0,0 +1,904 @@
@extends('frontend.layouts.app')
@section('title', 'Profil Ayarları — Animexe')
@push('styles')
<style>
.ps-wrap {
max-width: 860px;
margin: 0 auto;
padding: 40px 20px 80px;
}
/* Banner + Avatar */
.ps-banner-area {
position: relative;
height: 200px;
border-radius: 16px;
overflow: hidden;
background: linear-gradient(135deg, #0d0d1a, #1a0a2e);
margin-bottom: 0;
cursor: pointer;
transition: filter .2s;
}
.ps-banner-area:hover { filter: brightness(0.85); }
.ps-banner-img {
width: 100%; height: 100%;
object-fit: cover;
}
.ps-banner-overlay {
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
background: rgba(0,0,0,.45);
opacity: 0;
transition: opacity .2s;
flex-direction: column; gap: 8px;
font-size: .85rem; color: #fff;
}
.ps-banner-area:hover .ps-banner-overlay { opacity: 1; }
.ps-banner-area::after {
content: '';
position: absolute; inset: 0;
background: linear-gradient(to bottom, transparent 60%, rgba(0,0,0,.7));
pointer-events: none;
}
.ps-avatar-row {
display: flex;
align-items: flex-end;
gap: 20px;
margin-top: -48px;
padding: 0 24px;
margin-bottom: 24px;
position: relative;
z-index: 2;
}
.ps-avatar-wrap {
position: relative;
flex-shrink: 0;
cursor: pointer;
}
.ps-avatar {
width: 96px; height: 96px;
border-radius: 20px;
border: 3px solid var(--ps-color, #00f5ff);
object-fit: cover;
background: #1a1a2e;
display: flex; align-items: center; justify-content: center;
font-size: 2.2rem; font-weight: 900; color: #fff;
overflow: hidden;
box-shadow: 0 8px 32px rgba(0,0,0,.5);
transition: border-color .3s;
}
.ps-avatar img { width: 100%; height: 100%; object-fit: cover; }
.ps-avatar-overlay {
position: absolute; inset: 0;
border-radius: 18px;
background: rgba(0,0,0,.55);
display: flex; align-items: center; justify-content: center;
opacity: 0;
transition: opacity .2s;
font-size: 1.2rem; color: #fff;
}
.ps-avatar-wrap:hover .ps-avatar-overlay { opacity: 1; }
.ps-avatar-name {
padding-bottom: 8px;
font-size: 1.3rem;
font-weight: 800;
color: #fff;
}
/* Cards */
.ps-card {
background: rgba(255,255,255,.04);
border: 1px solid rgba(255,255,255,.08);
border-radius: 16px;
padding: 24px;
margin-bottom: 20px;
}
.ps-card-title {
font-size: .8rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .08em;
color: rgba(255,255,255,.4);
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255,255,255,.06);
display: flex;
align-items: center;
gap: 8px;
}
.ps-card-title i { font-size: 1rem; }
/* Form elements */
.ps-label {
display: block;
font-size: .8rem;
color: rgba(255,255,255,.5);
margin-bottom: 6px;
font-weight: 500;
}
.ps-input {
width: 100%;
background: rgba(255,255,255,.06);
border: 1px solid rgba(255,255,255,.1);
border-radius: 10px;
padding: 10px 14px;
color: #fff;
font-size: .9rem;
transition: border-color .2s, background .2s;
outline: none;
}
.ps-input:focus {
border-color: var(--ps-color, #00f5ff);
background: rgba(255,255,255,.09);
}
.ps-input::placeholder { color: rgba(255,255,255,.25); }
.ps-input.with-prefix { padding-left: 36px; }
.ps-field { margin-bottom: 16px; position: relative; }
.ps-prefix {
position: absolute;
left: 12px; top: 50%; transform: translateY(-50%);
color: rgba(255,255,255,.35);
font-size: .9rem;
pointer-events: none;
}
.ps-textarea {
resize: vertical;
min-height: 80px;
line-height: 1.5;
}
/* Color picker */
.ps-color-grid {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.ps-color-swatch {
width: 32px; height: 32px;
border-radius: 8px;
cursor: pointer;
border: 2px solid transparent;
transition: transform .15s, border-color .15s;
position: relative;
}
.ps-color-swatch:hover { transform: scale(1.15); }
.ps-color-swatch.active { border-color: #fff; transform: scale(1.1); }
.ps-color-swatch.active::after {
content: '✓';
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
font-size: .7rem; font-weight: 900; color: #fff;
text-shadow: 0 1px 3px rgba(0,0,0,.6);
}
.ps-color-custom {
width: 32px; height: 32px;
border-radius: 8px;
padding: 0;
border: 2px solid rgba(255,255,255,.2);
cursor: pointer;
overflow: hidden;
}
/* Privacy toggle */
.ps-toggle {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
border-bottom: 1px solid rgba(255,255,255,.05);
}
.ps-toggle:last-child { border-bottom: none; }
.ps-toggle-label { font-size: .88rem; color: rgba(255,255,255,.8); }
.ps-toggle-sub { font-size: .75rem; color: rgba(255,255,255,.35); margin-top: 2px; }
.ps-switch {
position: relative;
width: 44px; height: 24px;
flex-shrink: 0;
}
.ps-switch input { opacity: 0; width: 0; height: 0; }
.ps-slider {
position: absolute; inset: 0;
background: rgba(255,255,255,.12);
border-radius: 24px;
transition: background .2s;
cursor: pointer;
}
.ps-slider::before {
content: '';
position: absolute;
width: 18px; height: 18px;
left: 3px; top: 3px;
background: #fff;
border-radius: 50%;
transition: transform .2s;
}
.ps-switch input:checked + .ps-slider { background: var(--ps-color, #00f5ff); }
.ps-switch input:checked + .ps-slider::before { transform: translateX(20px); }
/* Save button */
.ps-save {
width: 100%;
padding: 13px;
border-radius: 12px;
background: var(--ps-color, #00f5ff);
color: #000;
font-weight: 800;
font-size: .95rem;
border: none;
cursor: pointer;
transition: opacity .2s, transform .1s;
letter-spacing: .02em;
}
.ps-save:hover { opacity: .88; }
.ps-save:active { transform: scale(.98); }
/* Alert */
.ps-alert {
border-radius: 10px;
padding: 12px 16px;
font-size: .85rem;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.ps-alert-success { background: rgba(63,185,80,.15); border: 1px solid rgba(63,185,80,.3); color: #7ee787; }
.ps-alert-error { background: rgba(248,81,73,.12); border: 1px solid rgba(248,81,73,.3); color: #f85149; }
/* Back link */
.ps-back {
display: inline-flex;
align-items: center;
gap: 7px;
color: rgba(255,255,255,.45);
font-size: .83rem;
margin-bottom: 28px;
text-decoration: none;
transition: color .2s;
}
.ps-back:hover { color: rgba(255,255,255,.8); }
/* Danger zone */
.ps-danger { border-color: rgba(248,81,73,.25); }
.ps-danger .ps-card-title { color: rgba(248,81,73,.7); }
@media (max-width: 600px) {
.ps-banner-area { height: 140px; }
.ps-avatar { width: 76px; height: 76px; }
.ps-avatar-row { margin-top: -38px; }
}
</style>
@endpush
@section('content')
<div class="ps-wrap" id="psWrap" style="--ps-color: {{ $user->profile_color ?? '#00f5ff' }}">
<a href="{{ route('profile') }}" class="ps-back">
<i class="bi bi-arrow-left"></i> Profilime Dön
</a>
@if(session('success'))
<div class="ps-alert ps-alert-success">
<i class="bi bi-check-circle-fill"></i>{{ session('success') }}
</div>
@endif
@if($errors->any())
<div class="ps-alert ps-alert-error">
<i class="bi bi-exclamation-circle-fill"></i>{{ $errors->first() }}
</div>
@endif
{{-- Banner --}}
<form method="POST" action="{{ route('profile.banner') }}" enctype="multipart/form-data" id="bannerForm">
@csrf
<div class="ps-banner-area" onclick="document.getElementById('bannerInput').click()" title="Kapak fotoğrafı değiştir">
@if($user->banner_image)
<img class="ps-banner-img" src="{{ \App\Support\MediaUrl::fromStoragePath($user->banner_image) }}" alt="Banner">
@else
<div class="ps-banner-img" style="background:linear-gradient(135deg,#0d0d1a 0%,#1a0a2e 50%,#0a1a1a 100%);display:flex;align-items:center;justify-content:center">
<span style="color:rgba(255,255,255,.1);font-size:3rem"><i class="bi bi-image"></i></span>
</div>
@endif
<div class="ps-banner-overlay">
<i class="bi bi-camera" style="font-size:1.5rem"></i>
Kapak Fotoğrafı Değiştir
</div>
</div>
<input type="file" id="bannerInput" name="banner" accept="image/*" style="display:none" onchange="this.form.submit()">
</form>
{{-- Avatar row --}}
<div class="ps-avatar-row">
<form method="POST" action="{{ route('profile.avatar') }}" enctype="multipart/form-data" id="avatarForm">
@csrf
<div class="ps-avatar-wrap" onclick="document.getElementById('avatarInput').click()" title="Fotoğraf değiştir">
<div class="ps-avatar">
@if($user->avatar)
<img src="{{ \App\Support\MediaUrl::fromStoragePath($user->avatar) }}" alt="{{ $user->name }}">
@else
{{ strtoupper(substr($user->name, 0, 1)) }}
@endif
</div>
<div class="ps-avatar-overlay"><i class="bi bi-camera-fill"></i></div>
</div>
<input type="file" id="avatarInput" name="avatar" accept="image/*" style="display:none" onchange="this.form.submit()">
</form>
<div class="ps-avatar-name">{{ $user->name }}</div>
</div>
{{-- Ana bilgiler --}}
<form method="POST" action="{{ route('profile.update') }}">
@csrf
<input type="hidden" name="profile_color" id="profileColorInput" value="{{ $user->profile_color ?? '#00f5ff' }}">
<div class="ps-card">
<div class="ps-card-title"><i class="bi bi-person"></i> Temel Bilgiler</div>
<div class="row g-3">
<div class="col-md-6">
<div class="ps-field">
<label class="ps-label">Görünen Ad *</label>
<input type="text" name="name" class="ps-input" value="{{ old('name', $user->name) }}" required maxlength="60">
</div>
</div>
<div class="col-md-6">
<div class="ps-field">
<label class="ps-label">Kullanıcı Adı <span style="opacity:.5">(opsiyonel)</span></label>
<div style="position:relative">
<span class="ps-prefix">@</span>
<input type="text" name="username" class="ps-input with-prefix"
value="{{ old('username', $user->username) }}"
placeholder="kullanici_adi" maxlength="30"
pattern="[a-zA-Z0-9_\-]+" title="Harf, rakam, _ veya - olabilir">
</div>
</div>
</div>
<div class="col-12">
<div class="ps-field">
<label class="ps-label">Hakkımda <span style="opacity:.5">(max 300 karakter)</span></label>
<textarea name="bio" class="ps-input ps-textarea" maxlength="300"
placeholder="Kendinizden kısaca bahsedin...">{{ old('bio', $user->bio) }}</textarea>
<div style="text-align:right;font-size:.72rem;color:rgba(255,255,255,.3);margin-top:4px">
<span id="bioCount">{{ strlen($user->bio ?? '') }}</span>/300
</div>
</div>
</div>
</div>
</div>
{{-- Sosyal linkler --}}
<div class="ps-card">
<div class="ps-card-title"><i class="bi bi-link-45deg"></i> Sosyal Bağlantılar</div>
<div class="row g-3">
<div class="col-md-6">
<div class="ps-field">
<label class="ps-label"><i class="bi bi-twitter-x" style="font-size:.9rem"></i> Twitter / X</label>
<div style="position:relative">
<span class="ps-prefix">@</span>
<input type="text" name="twitter" class="ps-input with-prefix"
value="{{ old('twitter', $user->twitter) }}"
placeholder="kullanici_adi" maxlength="50">
</div>
</div>
</div>
<div class="col-md-6">
<div class="ps-field">
<label class="ps-label"><i class="bi bi-instagram" style="font-size:.9rem"></i> Instagram</label>
<div style="position:relative">
<span class="ps-prefix">@</span>
<input type="text" name="instagram" class="ps-input with-prefix"
value="{{ old('instagram', $user->instagram) }}"
placeholder="kullanici_adi" maxlength="50">
</div>
</div>
</div>
<div class="col-md-6">
<div class="ps-field">
<label class="ps-label"><i class="bi bi-discord" style="font-size:.9rem"></i> Discord</label>
<input type="text" name="discord" class="ps-input"
value="{{ old('discord', $user->discord) }}"
placeholder="Kullanıcı#0000 veya kullanici" maxlength="80">
</div>
</div>
<div class="col-md-6">
<div class="ps-field">
<label class="ps-label"><i class="bi bi-globe2" style="font-size:.9rem"></i> Website</label>
<input type="url" name="website" class="ps-input"
value="{{ old('website', $user->website) }}"
placeholder="https://..." maxlength="200">
</div>
</div>
</div>
</div>
{{-- Profil rengi --}}
<div class="ps-card">
<div class="ps-card-title"><i class="bi bi-palette"></i> Profil Rengi</div>
<p style="font-size:.8rem;color:rgba(255,255,255,.4);margin-bottom:16px">
Profil sayfasındaki vurgu rengini seç.
</p>
<div class="ps-color-grid">
@php
$colors = [
'#00f5ff' => 'Siyan',
'#ff2d7d' => 'Pembe',
'#a371f7' => 'Mor',
'#3fb950' => 'Yeşil',
'#f0883e' => 'Turuncu',
'#ffd700' => 'Altın',
'#58a6ff' => 'Mavi',
'#ff6b6b' => 'Kırmızı',
'#e8d44d' => 'Sarı',
'#56d364' => 'Açık Yeşil',
];
$currentColor = old('profile_color', $user->profile_color ?? '#00f5ff');
@endphp
@foreach($colors as $hex => $name)
<div class="ps-color-swatch {{ $currentColor === $hex ? 'active' : '' }}"
style="background:{{ $hex }}"
title="{{ $name }}"
onclick="setColor('{{ $hex }}', this)"></div>
@endforeach
<input type="color" class="ps-color-custom" id="customColorPicker"
value="{{ $currentColor }}"
title="Özel renk"
onchange="setColor(this.value, null)">
</div>
<div style="margin-top:12px;font-size:.78rem;color:rgba(255,255,255,.3)">
Seçili: <span id="colorLabel" style="color:var(--ps-color)">{{ $currentColor }}</span>
</div>
</div>
{{-- Gizlilik --}}
<div class="ps-card">
<div class="ps-card-title"><i class="bi bi-shield-check"></i> Gizlilik</div>
<div class="ps-toggle">
<div>
<div class="ps-toggle-label">İzleme Listemi Göster</div>
<div class="ps-toggle-sub">Diğer kullanıcılar listenizi görebilir</div>
</div>
<label class="ps-switch">
<input type="checkbox" name="show_watchlist" value="1" {{ $user->show_watchlist ? 'checked' : '' }}>
<span class="ps-slider"></span>
</label>
</div>
<div class="ps-toggle">
<div>
<div class="ps-toggle-label">İzleme Aktivitemi Göster</div>
<div class="ps-toggle-sub">Izleme heatmap ve geçmişi herkese açık</div>
</div>
<label class="ps-switch">
<input type="checkbox" name="show_activity" value="1" {{ $user->show_activity ? 'checked' : '' }}>
<span class="ps-slider"></span>
</label>
</div>
</div>
<button type="submit" class="ps-save">
<i class="bi bi-check2 me-2"></i>Değişiklikleri Kaydet
</button>
</form>
{{-- Şifre değişikliği --}}
<form method="POST" action="{{ route('profile.password') }}" style="margin-top:20px">
@csrf
<div class="ps-card">
<div class="ps-card-title"><i class="bi bi-lock"></i> Şifre Değiştir</div>
<div class="row g-3">
<div class="col-12">
<div class="ps-field">
<label class="ps-label">Mevcut Şifre</label>
<input type="password" name="current_password" class="ps-input" required autocomplete="current-password">
</div>
</div>
<div class="col-md-6">
<div class="ps-field">
<label class="ps-label">Yeni Şifre</label>
<input type="password" name="password" class="ps-input" required autocomplete="new-password">
</div>
</div>
<div class="col-md-6">
<div class="ps-field">
<label class="ps-label">Yeni Şifre (Tekrar)</label>
<input type="password" name="password_confirmation" class="ps-input" required autocomplete="new-password">
</div>
</div>
</div>
<button type="submit" class="ps-save" style="margin-top:16px;background:rgba(255,255,255,.12);color:#fff">
<i class="bi bi-lock me-2"></i>Şifreyi Güncelle
</button>
</div>
</form>
{{-- ── Premium Kozmetik Ayarları ────────────────────────────────── --}}
@if(auth()->user()->isPremium())
<form method="POST" action="{{ route('premium.cosmetics.save') }}">
@csrf
@php $u = auth()->user(); @endphp
<div class="ps-card" style="border:1px solid rgba(0,245,255,.18);background:linear-gradient(135deg,rgba(0,245,255,.03),rgba(184,77,255,.03))">
<div class="ps-card-title" style="color:#00f5ff">
<i class="bi bi-stars"></i> Premium Kozmetik
<span style="font-size:.72rem;font-weight:400;color:rgba(0,245,255,.6);margin-left:8px">Sadece premium üyelere özel</span>
</div>
{{-- GIF Avatar --}}
@if($u->hasPerk('gif_avatar'))
<div class="ps-field" style="margin-bottom:20px">
<label class="ps-label"><i class="bi bi-image-fill" style="color:#00f5ff"></i> GIF Profil Fotoğrafı</label>
<input type="hidden" name="gif_avatar" id="gifAvatarInput" value="{{ old('gif_avatar', $u->gif_avatar) }}">
{{-- Mevcut seçim önizleme --}}
<div id="gifAvatarPreview" style="margin-bottom:10px;{{ $u->gif_avatar ? '' : 'display:none' }}">
<div style="display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:10px 14px;">
<img id="gifAvatarThumb" src="{{ $u->gif_avatar ?? '' }}" alt="GIF"
style="width:56px;height:56px;border-radius:10px;object-fit:cover;border:1px solid rgba(255,255,255,.12)">
<div style="flex:1;min-width:0">
<div style="font-size:.78rem;color:rgba(255,255,255,.7);margin-bottom:4px">Seçili GIF</div>
<div id="gifAvatarUrl" style="font-size:.65rem;color:rgba(255,255,255,.3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">{{ $u->gif_avatar ?? '' }}</div>
</div>
<button type="button" onclick="clearGifAvatar()" style="background:rgba(255,80,80,.12);border:1px solid rgba(255,80,80,.25);color:#ff5050;border-radius:8px;padding:5px 10px;font-size:.75rem;cursor:pointer;flex-shrink:0">
<i class="bi bi-x-lg"></i> Kaldır
</button>
</div>
</div>
{{-- Arama --}}
<div style="display:flex;gap:8px;margin-bottom:8px">
<input type="text" id="gifAvatarSearch" class="ps-input" placeholder="anime gif ara..." style="flex:1"
oninput="debounceGifSearch(this.value)">
<button type="button" onclick="debounceGifSearch(document.getElementById('gifAvatarSearch').value,true)"
style="background:rgba(0,245,255,.1);border:1px solid rgba(0,245,255,.25);color:#00f5ff;border-radius:10px;padding:0 14px;font-size:.85rem;cursor:pointer;flex-shrink:0;white-space:nowrap">
<i class="bi bi-search"></i>
</button>
</div>
{{-- Sonuç grid --}}
<div id="gifAvatarGrid" style="display:grid;grid-template-columns:repeat(4,1fr);gap:6px;max-height:280px;overflow-y:auto;scrollbar-width:thin"></div>
<div id="gifAvatarMsg" style="font-size:.72rem;color:rgba(255,255,255,.3);margin-top:6px;text-align:center"></div>
</div>
@endif
{{-- Yorum Arkaplanı --}}
@if($u->hasPerk('comment_bg'))
<div class="ps-field" style="margin-bottom:24px">
<label class="ps-label"><i class="bi bi-fire" style="color:#ff6b35"></i> Yorum Arkaplanı Efekti</label>
<div class="prem-picker" style="grid-template-columns:repeat(auto-fill,minmax(100px,1fr))">
<label class="prem-pick-item {{ !$u->comment_bg ? 'active' : '' }}">
<input type="radio" name="comment_bg" value="">
<div style="width:100%;height:36px;border-radius:7px;background:rgba(255,255,255,.05);display:flex;align-items:center;justify-content:center;font-size:.75rem;color:rgba(255,255,255,.3)"> Yok </div>
<span class="prem-pick-lbl">Yok</span>
</label>
@foreach(\App\Services\PremiumFeatures::COMMENT_BACKGROUNDS as $key => $bg)
<label class="prem-pick-item {{ $u->comment_bg === $key ? 'active' : '' }}">
<input type="radio" name="comment_bg" value="{{ $key }}">
<div class="comment-wrap" data-bg="{{ $key }}" style="width:100%;min-height:36px;border-radius:7px;display:flex;align-items:center;justify-content:center;font-size:.75rem;overflow:hidden">
<span>{{ $bg['label'] }}</span>
</div>
<span class="prem-pick-lbl">{{ $bg['label'] }}</span>
</label>
@endforeach
</div>
</div>
@endif
{{-- Kullanıcı Adı Rengi --}}
@if($u->hasPerk('username_color'))
<div class="ps-field" style="margin-bottom:24px">
<label class="ps-label"><i class="bi bi-palette-fill" style="color:#b84dff"></i> Kullanıcı Adı Rengi</label>
<div class="prem-picker" style="grid-template-columns:repeat(auto-fill,minmax(120px,1fr))">
<label class="prem-pick-item {{ !$u->username_color ? 'active' : '' }}">
<input type="radio" name="username_color" value="">
<span class="prem-pick-preview" style="color:rgba(200,200,220,.55)">{{ $u->name }}</span>
<span class="prem-pick-lbl">Varsayılan</span>
</label>
@foreach(\App\Services\PremiumFeatures::USERNAME_COLORS as $key => $color)
<label class="prem-pick-item {{ $u->username_color === $key ? 'active' : '' }}">
<input type="radio" name="username_color" value="{{ $key }}">
<span class="prem-pick-preview username-color-{{ $key }}">{{ $u->name }}</span>
<span class="prem-pick-lbl">{{ $color['label'] }}</span>
</label>
@endforeach
</div>
</div>
@endif
{{-- Profil Çerçevesi --}}
@if($u->hasPerk('profile_frame'))
<div class="ps-field" style="margin-bottom:24px">
<label class="ps-label"><i class="bi bi-circle-fill" style="color:#ffd700"></i> Profil Çerçevesi</label>
<div class="prem-picker" style="grid-template-columns:repeat(auto-fill,minmax(82px,1fr))">
<label class="prem-pick-item {{ !$u->profile_frame ? 'active' : '' }}">
<input type="radio" name="profile_frame" value="">
<div style="width:48px;height:48px;border-radius:50%;background:rgba(255,255,255,.07);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:1.1rem;color:rgba(255,255,255,.3)">{{ mb_substr($u->name,0,1) }}</div>
<span class="prem-pick-lbl">Yok</span>
</label>
@foreach(\App\Services\PremiumFeatures::PROFILE_FRAMES as $key => $frame)
<label class="prem-pick-item {{ $u->profile_frame === $key ? 'active' : '' }}">
<input type="radio" name="profile_frame" value="{{ $key }}">
<div class="avatar-wrap" style="width:48px;height:48px">
<div class="avatar-frame frame-{{ $key }}"></div>
<div style="width:48px;height:48px;border-radius:50%;background:rgba(255,255,255,.12);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:1.1rem;color:rgba(255,255,255,.6);position:relative;z-index:3">{{ mb_substr($u->name,0,1) }}</div>
</div>
<span class="prem-pick-lbl">{{ $frame['label'] }}</span>
</label>
@endforeach
</div>
</div>
@endif
{{-- Özel Rozet/Unvan --}}
@if($u->hasPerk('profile_badge'))
<div class="ps-field" style="margin-bottom:24px">
<label class="ps-label"><i class="bi bi-award-fill" style="color:#ffd700"></i> Özel Rozet / Unvan</label>
<input type="text" name="profile_badge" class="ps-input" value="{{ old('profile_badge', $u->profile_badge) }}"
placeholder="Anime Uzmanı" maxlength="32">
<div style="font-size:.72rem;color:rgba(255,255,255,.35);margin-top:4px">Max 32 karakter. Kullanıcı adının yanında görünür.</div>
</div>
@endif
{{-- Profil Müziği --}}
@if($u->hasPerk('profile_music'))
<div class="ps-field" style="margin-bottom:24px">
<label class="ps-label"><i class="bi bi-music-note-beamed" style="color:#00f5ff"></i> Profil Müziği URL</label>
<input type="url" name="profile_music_url" class="ps-input" value="{{ old('profile_music_url', $u->profile_music_url) }}"
placeholder="https://www.youtube.com/watch?v=... veya direkt MP3 URL">
<div style="font-size:.72rem;color:rgba(255,255,255,.35);margin-top:4px">YouTube, SoundCloud veya direkt ses dosyası URL'si. Profilini ziyaret edenlere çalar.</div>
</div>
@endif
{{-- Profil Arka Planı --}}
@if($u->hasPerk('profile_bg'))
<div class="ps-field" style="margin-bottom:24px">
<label class="ps-label"><i class="bi bi-stars" style="color:#b84dff"></i> Profil Arka Planı</label>
<div class="prem-picker" style="grid-template-columns:repeat(auto-fill,minmax(100px,1fr))">
<label class="prem-pick-item {{ !$u->profile_bg ? 'active' : '' }}">
<input type="radio" name="profile_bg" value="">
<div style="width:100%;height:36px;border-radius:7px;background:rgba(255,255,255,.05);display:flex;align-items:center;justify-content:center;font-size:.75rem;color:rgba(255,255,255,.3)"> Yok </div>
<span class="prem-pick-lbl">Varsayılan</span>
</label>
@foreach(\App\Services\PremiumFeatures::PROFILE_BACKGROUNDS as $key => $bg)
@php [$c1,$c2] = explode(',', $bg['preview']); @endphp
<label class="prem-pick-item {{ $u->profile_bg === $key ? 'active' : '' }}">
<input type="radio" name="profile_bg" value="{{ $key }}">
<div style="width:100%;height:36px;border-radius:7px;background:linear-gradient(135deg,{{ $c1 }},{{ $c2 }});display:flex;align-items:center;justify-content:center;font-size:.72rem;color:rgba(255,255,255,.8);font-weight:700">{{ $bg['label'] }}</div>
<span class="prem-pick-lbl">{{ $bg['label'] }}</span>
</label>
@endforeach
</div>
</div>
@endif
{{-- Yorum Aura / Parıltı --}}
@if($u->hasPerk('comment_glow'))
<div class="ps-field" style="margin-bottom:24px">
<label class="ps-label"><i class="bi bi-brightness-high-fill" style="color:#00f5ff"></i> Yorum Aura / Parıltı</label>
<div class="prem-picker" style="grid-template-columns:repeat(auto-fill,minmax(90px,1fr))">
<label class="prem-pick-item {{ !$u->comment_glow ? 'active' : '' }}">
<input type="radio" name="comment_glow" value="">
<div style="width:44px;height:44px;border-radius:50%;border:1.5px solid rgba(255,255,255,.15);display:flex;align-items:center;justify-content:center;font-size:.7rem;color:rgba(255,255,255,.3)"></div>
<span class="prem-pick-lbl">Yok</span>
</label>
@foreach(\App\Services\PremiumFeatures::COMMENT_GLOWS as $key => $glow)
<label class="prem-pick-item {{ $u->comment_glow === $key ? 'active' : '' }}">
<input type="radio" name="comment_glow" value="{{ $key }}">
<div style="width:44px;height:44px;border-radius:50%;border:1.5px solid {{ $glow['color'] }};box-shadow:0 0 12px {{ $glow['color'] }},0 0 24px {{ $glow['color'] }}44;background:{{ $glow['color'] }}22"></div>
<span class="prem-pick-lbl">{{ $glow['label'] }}</span>
</label>
@endforeach
</div>
</div>
@endif
{{-- Kullanıcı Adı Animasyonu --}}
@if($u->hasPerk('username_effect'))
<div class="ps-field" style="margin-bottom:24px">
<label class="ps-label"><i class="bi bi-lightning-charge-fill" style="color:#ffd700"></i> Kullanıcı Adı Animasyonu</label>
<div class="prem-picker" style="grid-template-columns:repeat(auto-fill,minmax(110px,1fr))">
<label class="prem-pick-item {{ !$u->username_effect ? 'active' : '' }}">
<input type="radio" name="username_effect" value="">
<span class="prem-pick-preview" style="color:rgba(200,200,220,.6)">{{ $u->name }}</span>
<span class="prem-pick-lbl">Yok</span>
</label>
@foreach(\App\Services\PremiumFeatures::USERNAME_EFFECTS as $key => $eff)
<label class="prem-pick-item {{ $u->username_effect === $key ? 'active' : '' }}">
<input type="radio" name="username_effect" value="{{ $key }}">
<span class="prem-pick-preview username-effect-{{ $key }}" @if($key==='glitch') data-text="{{ $u->name }}" @endif>{{ $u->name }}</span>
<span class="prem-pick-lbl">{{ $eff['label'] }}</span>
</label>
@endforeach
</div>
</div>
@endif
{{-- Yorum İmzası --}}
@if($u->hasPerk('comment_signature'))
<div class="ps-field" style="margin-bottom:24px">
<label class="ps-label"><i class="bi bi-pen-fill" style="color:#ff9ec4"></i> Yorum İmzası</label>
<input type="text" name="comment_signature" class="ps-input" value="{{ old('comment_signature', $u->comment_signature) }}"
placeholder="— Anime severinden sevgiyle ✨" maxlength="100">
<div style="font-size:.72rem;color:rgba(255,255,255,.35);margin-top:4px">Max 100 karakter. Her yorumun altında küçük yazıyla görünür.</div>
</div>
@endif
{{-- Sayfa Giriş Efekti --}}
@if($u->hasPerk('entry_effect'))
<div class="ps-field" style="margin-bottom:24px">
<label class="ps-label"><i class="bi bi-play-circle-fill" style="color:#3fb950"></i> Sayfa Giriş Efekti</label>
<div class="prem-picker" style="grid-template-columns:repeat(auto-fill,minmax(100px,1fr))">
<label class="prem-pick-item {{ !$u->entry_effect ? 'active' : '' }}">
<input type="radio" name="entry_effect" value="">
<div style="font-size:1.4rem">🚫</div>
<span class="prem-pick-lbl">Yok</span>
</label>
@foreach(\App\Services\PremiumFeatures::ENTRY_EFFECTS as $key => $eff)
<label class="prem-pick-item {{ $u->entry_effect === $key ? 'active' : '' }}">
<input type="radio" name="entry_effect" value="{{ $key }}">
<div style="font-size:1.4rem">{{ ['fade'=>'✨','slide'=>'⬆️','zoom'=>'🔍','glitch'=>'⚡','wave'=>'🌊'][$key] ?? '✨' }}</div>
<span class="prem-pick-lbl">{{ $eff['label'] }}</span>
</label>
@endforeach
</div>
</div>
@endif
{{-- Animasyonlu Banner --}}
@if($u->hasPerk('animated_banner'))
<div class="ps-field" style="margin-bottom:24px">
<div class="ps-toggle" style="padding:14px 16px;background:rgba(255,255,255,.04);border-radius:12px;border:1px solid rgba(255,255,255,.08);border-bottom:1px solid rgba(255,255,255,.08)">
<div>
<div class="ps-toggle-label" style="color:#fff"><i class="bi bi-image-alt" style="color:#00f5ff;margin-right:6px"></i> Animasyonlu Profil Bannerı</div>
<div class="ps-toggle-sub">Profil bannerın parıltılı parçacık efektiyle canlanır</div>
</div>
<label class="ps-switch">
<input type="checkbox" name="animated_banner" value="1" {{ $u->animated_banner ? 'checked' : '' }}>
<span class="ps-slider"></span>
</label>
</div>
</div>
@endif
<button type="submit" class="ps-save" style="background:linear-gradient(135deg,rgba(0,245,255,.15),rgba(184,77,255,.15));border:1px solid rgba(0,245,255,.3);color:#00f5ff">
<i class="bi bi-stars me-2"></i>Premium Ayarları Kaydet
</button>
</div>
</form>
@else
{{-- Premium değil yükseltme banner'ı --}}
<div class="ps-card" style="border:1px solid rgba(255,215,0,.2);background:linear-gradient(135deg,rgba(255,215,0,.04),rgba(255,140,0,.04));text-align:center;padding:32px 24px">
<div style="font-size:2rem;margin-bottom:12px"></div>
<div style="font-weight:700;font-size:1rem;color:#ffd700;margin-bottom:6px">Premium Kozmetikler</div>
<div style="font-size:.85rem;color:rgba(255,255,255,.5);margin-bottom:16px">
Animasyonlu yorum arkaplanları, renkli kullanıcı adı, profil çerçevesi ve daha fazlası için premium üye ol.
</div>
<a href="{{ route('premium.plans') }}" class="ps-save" style="display:inline-flex;background:linear-gradient(135deg,#ff8c00,#ffd700);color:#000;font-weight:800;text-decoration:none;padding:10px 28px;border-radius:10px">
<i class="bi bi-stars me-2"></i>Premium'a Geç
</a>
</div>
@endif
</div>
@endsection
@push('scripts')
<script>
// Premium picker: radio seçilince parent label'a .active sınıfı ekle
document.querySelectorAll('.prem-picker input[type="radio"]').forEach(radio => {
radio.addEventListener('change', () => {
const picker = radio.closest('.prem-picker');
picker.querySelectorAll('.prem-pick-item').forEach(el => el.classList.remove('active'));
radio.closest('.prem-pick-item').classList.add('active');
});
});
const wrap = document.getElementById('psWrap');
function setColor(hex, el) {
wrap.style.setProperty('--ps-color', hex);
document.getElementById('profileColorInput').value = hex;
document.getElementById('colorLabel').textContent = hex;
document.getElementById('colorLabel').style.color = hex;
document.getElementById('customColorPicker').value = hex;
document.querySelectorAll('.ps-color-swatch').forEach(s => s.classList.remove('active'));
if (el) el.classList.add('active');
}
// Bio karakter sayacı
const bioTextarea = document.querySelector('[name="bio"]');
const bioCount = document.getElementById('bioCount');
if (bioTextarea) {
bioTextarea.addEventListener('input', () => {
bioCount.textContent = bioTextarea.value.length;
});
}
// Banner önizleme
document.getElementById('bannerInput').addEventListener('change', function() {
const file = this.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = e => {
const img = document.querySelector('.ps-banner-img');
if (img.tagName === 'IMG') {
img.src = e.target.result;
} else {
const newImg = document.createElement('img');
newImg.className = 'ps-banner-img';
newImg.src = e.target.result;
img.replaceWith(newImg);
}
};
reader.readAsDataURL(file);
});
// Avatar önizleme
document.getElementById('avatarInput').addEventListener('change', function() {
const file = this.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = e => {
const avatarDiv = document.querySelector('.ps-avatar');
avatarDiv.innerHTML = `<img src="${e.target.result}" alt="Avatar">`;
};
reader.readAsDataURL(file);
});
// ── GIF Avatar Picker ─────────────────────────────────────────────────────
let gifSearchTimer = null;
function debounceGifSearch(q, immediate = false) {
clearTimeout(gifSearchTimer);
if (!q.trim()) { document.getElementById('gifAvatarGrid').innerHTML = ''; return; }
const delay = immediate ? 0 : 500;
gifSearchTimer = setTimeout(() => searchGifAvatar(q.trim()), delay);
}
async function searchGifAvatar(q) {
const grid = document.getElementById('gifAvatarGrid');
const msg = document.getElementById('gifAvatarMsg');
grid.innerHTML = '<div style="grid-column:1/-1;text-align:center;padding:20px;color:rgba(255,255,255,.3);font-size:.8rem"><i class="bi bi-arrow-repeat"></i> Aranıyor...</div>';
msg.textContent = '';
try {
const res = await fetch(`/comments/gif-search?q=${encodeURIComponent(q)}`);
const data = await res.json();
const gifs = data.results ?? [];
if (!gifs.length) {
grid.innerHTML = '';
msg.textContent = 'Sonuç bulunamadı.';
return;
}
grid.innerHTML = gifs.map(g => `
<div onclick="selectGifAvatar('${g.url.replace(/'/g,"\\'")}','${g.preview.replace(/'/g,"\\'")}','${(g.url).replace(/'/g,"\\'")}' )"
style="cursor:pointer;border-radius:8px;overflow:hidden;border:2px solid transparent;transition:border-color .15s,transform .15s;aspect-ratio:1;background:#111"
onmouseover="this.style.borderColor='rgba(0,245,255,.5)';this.style.transform='scale(1.04)'"
onmouseout="this.style.borderColor='transparent';this.style.transform='scale(1)'">
<img src="${g.preview}" alt="" loading="lazy" style="width:100%;height:100%;object-fit:cover;display:block">
</div>`).join('');
} catch(e) {
grid.innerHTML = '';
msg.textContent = 'Arama başarısız oldu.';
}
}
function selectGifAvatar(url, preview) {
document.getElementById('gifAvatarInput').value = url;
document.getElementById('gifAvatarThumb').src = preview;
document.getElementById('gifAvatarUrl').textContent = url;
document.getElementById('gifAvatarPreview').style.display = '';
document.getElementById('gifAvatarGrid').innerHTML = '';
document.getElementById('gifAvatarSearch').value = '';
document.getElementById('gifAvatarMsg').textContent = '';
}
function clearGifAvatar() {
document.getElementById('gifAvatarInput').value = '';
document.getElementById('gifAvatarPreview').style.display = 'none';
}
</script>
@endpush
+825
View File
@@ -0,0 +1,825 @@
@extends('frontend.layouts.app')
@section('title', $user->name . ' — Profil — Animexe')
@push('styles')
<style>
/* ═══════════════════════════ PROFILE ═══════════════════════════ */
/* ── Hero banner ──────────────────────────────────────────────── */
.pr-hero{
position:relative;overflow:hidden;
background:#05050e;
padding-bottom:0;
}
.pr-hero-bg{
position:absolute;inset:0;
background:
radial-gradient(ellipse 80% 60% at 20% 50%, rgba(0,245,255,.06), transparent),
radial-gradient(ellipse 60% 80% at 80% 30%, rgba(255,45,125,.05), transparent);
}
.pr-hero-inner{
position:relative;z-index:1;
max-width:960px;margin:0 auto;
padding:calc(var(--nav-h) + 32px) 24px 0;
}
.pr-top{display:flex;align-items:flex-start;gap:20px;flex-wrap:wrap;margin-bottom:28px}
.pr-top-main{display:flex;align-items:flex-end;gap:20px;flex:1;min-width:0}
/* ── Avatar ───────────────────────────────────────────────────── */
.pr-avatar{
width:96px;height:96px;border-radius:24px;flex-shrink:0;
background:linear-gradient(135deg,var(--accent2),var(--accent));
display:flex;align-items:center;justify-content:center;
font-size:2.4rem;font-weight:900;color:#fff;overflow:hidden;
box-shadow:0 0 0 3px rgba(0,245,255,.2),0 16px 48px rgba(0,0,0,.5);
}
.pr-info{flex:1;min-width:0;padding-bottom:4px}
.pr-name{font-size:1.7rem;font-weight:900;color:#fff;line-height:1.1;margin-bottom:4px}
.pr-handle{font-size:.84rem;color:rgba(255,255,255,.35);margin-bottom:10px}
.pr-badges{display:flex;gap:7px;flex-wrap:wrap}
.pbadge{padding:4px 12px;border-radius:20px;font-size:.7rem;font-weight:700;letter-spacing:.05em;display:inline-flex;align-items:center;gap:5px}
.pbadge-admin{background:rgba(255,45,125,.15);border:1px solid rgba(255,45,125,.35);color:var(--accent)}
.pbadge-premium{background:rgba(255,208,0,.12);border:1px solid rgba(255,208,0,.3);color:#ffd700}
.pbadge-member{background:rgba(0,245,255,.07);border:1px solid rgba(0,245,255,.2);color:var(--accent2)}
/* ── Stat pills ───────────────────────────────────────────────── */
.pr-stats{
display:flex;gap:6px;flex-wrap:wrap;
padding:20px 0 0;
border-top:1px solid rgba(255,255,255,.06);
margin-top:8px;
}
.pr-stat{
display:flex;flex-direction:column;align-items:center;
padding:10px 20px;border-radius:12px;
background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);
min-width:80px;flex:1;
}
.pr-stat .v{font-size:1.25rem;font-weight:800;color:#fff;line-height:1}
.pr-stat .l{font-size:.62rem;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,.35);margin-top:4px;font-weight:600}
/* ── Tab nav ──────────────────────────────────────────────────── */
.pr-tabs{
display:flex;gap:2px;
border-top:1px solid rgba(255,255,255,.06);
margin-top:20px;
overflow-x:auto;scrollbar-width:none;
}
.pr-tabs::-webkit-scrollbar{display:none}
.pr-tab{
padding:12px 18px;background:none;border:none;
border-bottom:2px solid transparent;
color:rgba(255,255,255,.35);font-size:.82rem;font-weight:600;
cursor:pointer;font-family:inherit;transition:all .15s;
white-space:nowrap;flex-shrink:0;display:flex;align-items:center;gap:6px;
margin-bottom:-1px;
}
.pr-tab:hover{color:rgba(255,255,255,.65)}
.pr-tab.on{color:var(--accent2);border-bottom-color:var(--accent2)}
/* ── Body ─────────────────────────────────────────────────────── */
.pr-body{max-width:960px;margin:0 auto;padding:32px 24px 64px}
.pr-pane{display:none}
.pr-pane.on{display:block}
/* ── Section header ───────────────────────────────────────────── */
.pr-sec-h{
font-size:.68rem;text-transform:uppercase;letter-spacing:.12em;
color:rgba(255,255,255,.3);font-weight:700;
margin-bottom:16px;display:flex;align-items:center;gap:8px;
}
.pr-sec-h::before{content:'';width:3px;height:14px;background:linear-gradient(180deg,var(--accent2),var(--accent));border-radius:2px}
/* ── Continue watching cards ──────────────────────────────────── */
.cw-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:12px}
.cw-card{
border-radius:10px;overflow:hidden;
background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);
text-decoration:none;display:block;
transition:transform .15s,border-color .15s;
}
.cw-card:hover{transform:translateY(-3px);border-color:rgba(0,245,255,.25)}
.cw-thumb{position:relative;aspect-ratio:2/3;overflow:hidden}
.cw-thumb img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .3s}
.cw-card:hover .cw-thumb img{transform:scale(1.05)}
.cw-thumb-ph{width:100%;height:100%;background:rgba(255,255,255,.04);display:flex;align-items:center;justify-content:center;color:rgba(255,255,255,.2);font-size:2rem}
.cw-grad{position:absolute;inset:0;background:linear-gradient(to top,rgba(0,0,0,.85) 0%,transparent 55%)}
.cw-play{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:38px;height:38px;border-radius:50%;background:rgba(255,45,125,.85);display:flex;align-items:center;justify-content:center}
.cw-play i{color:#fff;font-size:.95rem}
.cw-progress{position:absolute;bottom:28px;left:10px;right:10px}
.cw-prog-bar{height:3px;background:rgba(255,255,255,.2);border-radius:2px;overflow:hidden}
.cw-prog-fill{height:100%;background:var(--accent);border-radius:2px}
.cw-ep{position:absolute;bottom:8px;left:10px;font-size:.62rem;color:rgba(255,255,255,.6);font-weight:600}
.cw-info{padding:8px 10px}
.cw-info-title{font-size:.76rem;font-weight:600;color:rgba(255,255,255,.8);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ── Achievement grid ─────────────────────────────────────────── */
.ach-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px}
.ach-card{
display:flex;align-items:center;gap:14px;
background:rgba(255,255,255,.03);
border:1px solid rgba(255,255,255,.07);
border-radius:12px;padding:14px 16px;
transition:border-color .15s;
}
.ach-card.earned{background:rgba(255,255,255,.05)}
.ach-card:not(.earned){opacity:.35}
.ach-icon-wrap{width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:1.4rem;flex-shrink:0}
.ach-body .ach-title{font-size:.85rem;font-weight:700;color:#fff;margin-bottom:2px}
.ach-body .ach-desc{font-size:.72rem;color:rgba(255,255,255,.35);line-height:1.4}
.ach-body .ach-date{font-size:.66rem;margin-top:4px;font-weight:600}
/* ── Watchlist tabs ───────────────────────────────────────────── */
.wl-filter{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:20px}
.wl-filter-btn{
padding:6px 14px;border-radius:20px;border:1px solid rgba(255,255,255,.1);
background:none;color:rgba(255,255,255,.45);font-size:.78rem;font-weight:600;
cursor:pointer;font-family:inherit;transition:all .15s;
}
.wl-filter-btn:hover{border-color:rgba(255,255,255,.25);color:rgba(255,255,255,.7)}
.wl-filter-btn.on{background:rgba(0,245,255,.1);border-color:rgba(0,245,255,.3);color:var(--accent2)}
.wl-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:10px}
.wl-card{
border-radius:9px;overflow:hidden;text-decoration:none;display:block;
background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);
transition:transform .15s,border-color .15s;
}
.wl-card:hover{transform:translateY(-2px);border-color:rgba(255,255,255,.18)}
.wl-card img{width:100%;aspect-ratio:2/3;object-fit:cover;display:block}
.wl-card-ph{aspect-ratio:2/3;background:rgba(255,255,255,.04);display:flex;align-items:center;justify-content:center;color:rgba(255,255,255,.2)}
.wl-card-title{padding:7px 8px;font-size:.72rem;font-weight:600;color:rgba(255,255,255,.7);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ── Heatmap ──────────────────────────────────────────────────── */
.heatmap-wrap{
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);
border-radius:14px;padding:20px 24px;
}
.heatmap-scroll{overflow-x:auto;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.1) transparent;padding-bottom:6px}
.heatmap-grid{display:flex;gap:3px;min-width:max-content}
.hm-col{display:flex;flex-direction:column;gap:3px}
.hm-cell{width:13px;height:13px;border-radius:3px}
.heatmap-legend{display:flex;align-items:center;gap:10px;margin-top:14px;font-size:.7rem;color:rgba(255,255,255,.3)}
.heatmap-legend-cells{display:flex;gap:3px;align-items:center}
.heatmap-stats{display:flex;gap:20px;margin-top:12px;font-size:.78rem;color:rgba(255,255,255,.4)}
/* ── Notes ────────────────────────────────────────────────────── */
.note-card{
background:rgba(255,255,255,.03);border:1px solid rgba(255,215,0,.12);
border-radius:12px;padding:16px 18px;margin-bottom:10px;
}
.note-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:8px}
.note-anime{font-size:.8rem;font-weight:700;color:var(--accent2);text-decoration:none}
.note-anime:hover{text-decoration:underline}
.note-ep{font-size:.72rem;color:rgba(255,255,255,.3)}
.note-ts{font-size:.72rem;color:#ffd700;display:inline-flex;align-items:center;gap:4px}
.note-date{font-size:.68rem;color:rgba(255,255,255,.25);margin-left:auto}
.note-text{font-size:.87rem;color:rgba(255,255,255,.65);line-height:1.6}
/* ── Comments ─────────────────────────────────────────────────── */
.cm-card{
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);
border-radius:12px;padding:14px 16px;margin-bottom:10px;
}
.cm-top{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.cm-ava{width:30px;height:30px;border-radius:10px;background:linear-gradient(135deg,var(--accent2),var(--accent));display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:800;color:#fff;flex-shrink:0}
.cm-name{font-size:.84rem;font-weight:700;color:rgba(255,255,255,.8)}
.cm-time{font-size:.72rem;color:rgba(255,255,255,.25);margin-left:auto}
.cm-text{font-size:.88rem;color:rgba(255,255,255,.6);line-height:1.6}
.cm-gif{max-width:200px;border-radius:8px;margin-top:8px}
.pr-empty{text-align:center;padding:56px 20px;color:rgba(255,255,255,.2);font-size:.9rem}
.pr-empty i{font-size:2.5rem;display:block;margin-bottom:12px;opacity:.25}
/* ── Responsive ───────────────────────────────────────────────── */
@media(max-width:600px){
.pr-hero-inner{padding:calc(var(--nav-h) + 16px) 16px 0}
.pr-body{padding:24px 16px 48px}
.pr-avatar{width:72px;height:72px;border-radius:18px;font-size:1.8rem}
.pr-name{font-size:1.3rem}
.pr-stat{padding:8px 12px;min-width:60px}
.pr-stat .v{font-size:1rem}
.ach-grid{grid-template-columns:1fr}
.cw-grid{grid-template-columns:repeat(auto-fill,minmax(110px,1fr))}
.pr-top{flex-direction:column;gap:14px}
.pr-top-main{align-items:flex-start}
.pr-edit-btn{align-self:stretch;justify-content:center}
}
/* ═══════════════════════════════════════════════════
PROFILE PAGE PREMIUM ENHANCEMENTS
═══════════════════════════════════════════════════ */
/* Avatar animated rainbow border */
@keyframes avatar-border-spin{
0%{background-position:0% 50%}
100%{background-position:200% 50%}
}
.pr-avatar{
position:relative;
}
.pr-avatar::before{
content:'';position:absolute;inset:-2px;border-radius:26px;z-index:-1;
background:linear-gradient(135deg,var(--accent2),var(--accent),var(--accent3),var(--accent2));
background-size:300% 300%;
animation:avatar-border-spin 3s linear infinite;
}
/* Username gradient */
.pr-name{
background:linear-gradient(135deg,#fff 0%,rgba(255,255,255,.85) 60%,rgba(240,244,255,.6) 100%);
-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;
}
/* Stats strip enhanced */
.pr-stats{
background:rgba(255,255,255,.02);
border-radius:14px;
padding:14px;
border:1px solid rgba(255,255,255,.05);
gap:8px;
}
.pr-stat{
border-radius:10px;
background:rgba(255,255,255,.04);
border:1px solid rgba(255,255,255,.07);
transition:all .2s;
}
.pr-stat:hover{
background:rgba(0,245,255,.06);
border-color:rgba(0,245,255,.2);
transform:translateY(-2px);
}
.pr-stat .v{
background:linear-gradient(135deg,var(--accent2),var(--accent));
-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;
font-size:1.35rem;
}
/* Tab enhanced */
.pr-tab.on{
color:var(--accent2);
text-shadow:0 0 12px rgba(0,245,255,.3);
}
/* Entrance animations */
@keyframes pr-fade-up{
from{opacity:0;transform:translateY(18px)}
to{opacity:1;transform:none}
}
.pr-avatar{animation:pr-fade-up .5s cubic-bezier(.22,1,.36,1) .1s both}
.pr-info{animation:pr-fade-up .5s cubic-bezier(.22,1,.36,1) .2s both}
.pr-stats{animation:pr-fade-up .5s cubic-bezier(.22,1,.36,1) .3s both}
.pr-tabs{animation:pr-fade-up .4s cubic-bezier(.22,1,.36,1) .4s both}
/* Achievement cards enhanced */
.ach-card.earned{
border:1px solid rgba(255,255,255,.1);
background:linear-gradient(135deg,rgba(255,255,255,.05),rgba(0,245,255,.02));
transition:all .2s;
}
.ach-card.earned:hover{
border-color:rgba(0,245,255,.2);
transform:translateY(-2px);
box-shadow:0 8px 24px rgba(0,0,0,.4),0 0 20px rgba(0,245,255,.05);
}
/* Watchlist cards enhanced */
.wl-card:hover{
transform:translateY(-3px);
border-color:rgba(0,245,255,.2);
box-shadow:0 8px 24px rgba(0,0,0,.4);
}
/* Heatmap wrap */
.heatmap-wrap{
background:linear-gradient(135deg,rgba(255,255,255,.03),rgba(0,245,255,.01));
border:1px solid rgba(0,245,255,.07);
}
/* Profile edit button */
.pr-edit-btn{
transition:all .18s;
}
.pr-edit-btn:hover{
background:rgba(0,245,255,.12);
border-color:rgba(0,245,255,.4);
color:var(--accent2);
box-shadow:0 0 20px rgba(0,245,255,.15);
}
/* Body panes reveal */
.pr-body{animation:pr-fade-up .5s cubic-bezier(.22,1,.36,1) .2s both}
/* Profile color accent on section headers */
.pr-sec-h::before{
box-shadow:0 0 10px rgba(0,245,255,.5);
}
/* ── Swipe history ───────────────────────────────────── */
.swipe-filter {
padding: 5px 13px; border-radius: 999px; font-size: .68rem;
background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08);
color: var(--text3); cursor: pointer; font-family: var(--font);
transition: all .15s; display: inline-flex; align-items: center; gap: 5px;
}
.swipe-filter:hover { border-color: rgba(0,245,255,.25); color: var(--text2); }
.swipe-filter.on { background: rgba(0,245,255,.1); border-color: var(--accent2); color: var(--accent2); }
.swipe-f-count {
font-size: .6rem; background: rgba(255,255,255,.08); border-radius: 999px;
padding: 1px 6px; color: var(--text3);
}
.swipe-filter.on .swipe-f-count { background: rgba(0,245,255,.15); color: var(--accent2); }
.swipe-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 10px;
}
.swipe-item {
text-decoration: none; border-radius: 12px; overflow: hidden;
background: var(--surface2); display: flex; flex-direction: column;
transition: transform .18s, box-shadow .18s;
}
.swipe-item:hover { transform: translateY(-3px); box-shadow: 0 8px 24px rgba(0,0,0,.4); }
.swipe-poster {
position: relative; aspect-ratio: 2/3; overflow: hidden;
}
.swipe-poster img { width: 100%; height: 100%; object-fit: cover; display: block; }
.swipe-poster-ph {
width: 100%; height: 100%;
display: flex; align-items: center; justify-content: center;
color: var(--text3); font-size: 1.5rem;
}
.swipe-dir-badge {
position: absolute; top: 6px; right: 6px;
width: 22px; height: 22px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: .6rem; font-weight: 700;
}
.badge-like { background: rgba(0,245,255,.18); color: var(--accent2); border: 1px solid rgba(0,245,255,.3); }
.badge-skip { background: rgba(255,45,125,.15); color: #ff2d7d; border: 1px solid rgba(255,45,125,.3); }
.swipe-info { padding: 7px 8px 9px; }
.swipe-title {
font-size: .68rem; font-weight: 600; color: var(--text);
line-height: 1.3; margin-bottom: 4px;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.swipe-meta { display: flex; gap: 6px; font-size: .6rem; color: var(--text3); flex-wrap: wrap; }
.swipe-item.skip .swipe-poster { opacity: .65; }
.swipe-item.skip .swipe-poster img { filter: grayscale(25%); }
</style>
@endpush
@section('content')
@php $pColor = $user->profile_color ?? '#00f5ff'; @endphp
{{-- Hero --}}
<div class="pr-hero" style="--pr-color:{{ $pColor }}">
{{-- Banner --}}
@if($user->banner_image)
<div style="position:absolute;inset:0;overflow:hidden">
<img src="{{ \App\Support\MediaUrl::fromStoragePath($user->banner_image) }}" alt=""
style="width:100%;height:100%;object-fit:cover;opacity:.35;filter:blur(1px)">
<div style="position:absolute;inset:0;background:linear-gradient(to bottom,rgba(5,5,14,.3) 0%,rgba(5,5,14,.85) 100%)"></div>
</div>
@else
<div class="pr-hero-bg" style="background:radial-gradient(ellipse 80% 60% at 20% 50%,{{ $pColor }}18,transparent),radial-gradient(ellipse 60% 80% at 80% 30%,{{ $pColor }}10,transparent)"></div>
@endif
<div class="pr-hero-inner">
{{-- Top row: avatar + info + edit button --}}
<div class="pr-top">
<div class="pr-top-main">
{{-- Avatar --}}
<div class="pr-avatar" style="background:linear-gradient(135deg,{{ $pColor }},{{ $pColor }}88);box-shadow:0 0 0 3px {{ $pColor }}33,0 16px 48px rgba(0,0,0,.5)">
@if($user->avatar)
<img src="{{ \App\Support\MediaUrl::fromStoragePath($user->avatar) }}" alt="{{ $user->name }}"
style="width:100%;height:100%;object-fit:cover;border-radius:22px">
@else
{{ mb_strtoupper(mb_substr($user->name,0,1)) }}
@endif
</div>
<div class="pr-info">
<div class="pr-name">{{ $user->name }}</div>
@if($user->username)
<div class="pr-handle">@php echo '@' . e($user->username); @endphp</div>
@endif
@if($user->bio)
<div style="font-size:.83rem;color:rgba(255,255,255,.55);margin-bottom:10px;max-width:500px;line-height:1.5">{{ $user->bio }}</div>
@endif
{{-- Sosyal linkler --}}
@if($user->twitter || $user->instagram || $user->discord || $user->website)
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-bottom:10px;align-items:center">
@if($user->twitter)
<a href="https://x.com/{{ $user->twitter }}" target="_blank" rel="noopener"
style="color:rgba(255,255,255,.5);font-size:.82rem;text-decoration:none;display:flex;align-items:center;gap:4px;transition:color .2s"
onmouseover="this.style.color='{{ $pColor }}'" onmouseout="this.style.color='rgba(255,255,255,.5)'">
<i class="bi bi-twitter-x"></i> {{ $user->twitter }}
</a>
@endif
@if($user->instagram)
<a href="https://instagram.com/{{ $user->instagram }}" target="_blank" rel="noopener"
style="color:rgba(255,255,255,.5);font-size:.82rem;text-decoration:none;display:flex;align-items:center;gap:4px;transition:color .2s"
onmouseover="this.style.color='{{ $pColor }}'" onmouseout="this.style.color='rgba(255,255,255,.5)'">
<i class="bi bi-instagram"></i> {{ $user->instagram }}
</a>
@endif
@if($user->discord)
<span style="color:rgba(255,255,255,.5);font-size:.82rem;display:flex;align-items:center;gap:4px">
<i class="bi bi-discord"></i> {{ $user->discord }}
</span>
@endif
@if($user->website)
<a href="{{ $user->website }}" target="_blank" rel="noopener"
style="color:rgba(255,255,255,.5);font-size:.82rem;text-decoration:none;display:flex;align-items:center;gap:4px;transition:color .2s"
onmouseover="this.style.color='{{ $pColor }}'" onmouseout="this.style.color='rgba(255,255,255,.5)'">
<i class="bi bi-globe2"></i> {{ parse_url($user->website, PHP_URL_HOST) }}
</a>
@endif
</div>
@endif
<div class="pr-badges">
@if($user->role==='admin')
<span class="pbadge pbadge-admin"><i class="bi bi-shield-fill"></i> Admin</span>
@elseif($user->role==='moderator')
<span class="pbadge pbadge-admin"><i class="bi bi-shield-half"></i> Moderatör</span>
@endif
@if($user->isPremium())
<span class="pbadge pbadge-premium"><i class="bi bi-star-fill"></i> Premium</span>
@else
<span class="pbadge pbadge-member"><i class="bi bi-person-check"></i> Üye</span>
@endif
<span class="pbadge" style="background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.1);color:rgba(255,255,255,.35)"><i class="bi bi-calendar3"></i> {{ $user->created_at->format('Y') }}'den beri</span>
</div>
</div>
</div>
{{-- Ayarlar butonu ayrı satırda, üstte sabit --}}
<a href="{{ route('profile.settings') }}" class="pr-edit-btn"
style="flex-shrink:0;padding:8px 16px;border-radius:10px;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);color:rgba(255,255,255,.6);font-size:.82rem;text-decoration:none;display:flex;align-items:center;gap:7px;transition:all .2s;white-space:nowrap;align-self:flex-start;margin-top:4px"
onmouseover="this.style.background='rgba(255,255,255,.12)';this.style.color='#fff'"
onmouseout="this.style.background='rgba(255,255,255,.07)';this.style.color='rgba(255,255,255,.6)'">
<i class="bi bi-pencil-square"></i> Profili Düzenle
</a>
</div>
{{-- Stats --}}
<div class="pr-stats">
<div class="pr-stat">
<span class="v">{{ $watchStats['episodes'] }}</span>
<span class="l">Bölüm</span>
</div>
<div class="pr-stat">
<span class="v">{{ $watchStats['hours'] }}</span>
<span class="l">Saat</span>
</div>
<div class="pr-stat">
<span class="v">{{ $watchStats['watchlist'] }}</span>
<span class="l">Listede</span>
</div>
<div class="pr-stat">
<span class="v">{{ $commentCount }}</span>
<span class="l">Yorum</span>
</div>
@if($allAchievements->count())
<div class="pr-stat">
<span class="v">{{ $achievements->count() }}</span>
<span class="l">Başarım</span>
</div>
@endif
</div>
{{-- Tab nav --}}
<div class="pr-tabs" id="pr-tabs">
@if($continueItems->count())
<button class="pr-tab on" data-pane="continue"><i class="bi bi-play-circle"></i> Devam Et</button>
@endif
<button class="pr-tab {{ !$continueItems->count() ? 'on' : '' }}" data-pane="watchlist"><i class="bi bi-bookmark"></i> Listem</button>
<button class="pr-tab" data-pane="activity"><i class="bi bi-graph-up"></i> Aktivite</button>
@if($allAchievements->count())
<button class="pr-tab" data-pane="achievements"><i class="bi bi-trophy"></i> Başarımlar</button>
@endif
<button class="pr-tab" data-pane="notes"><i class="bi bi-journal-text"></i> Notlar @if($episodeNotes->count())<span style="font-size:.68rem;background:rgba(0,245,255,.15);color:var(--accent2);border-radius:10px;padding:1px 6px;margin-left:2px">{{ $episodeNotes->count() }}</span>@endif</button>
<button class="pr-tab" data-pane="comments"><i class="bi bi-chat-dots"></i> Yorumlar</button>
<button class="pr-tab" data-pane="swipes"><i class="bi bi-hand-index-thumb"></i> Keşfet @if($swipeHistory->count())<span style="font-size:.65rem;background:rgba(0,245,255,.12);color:var(--accent2);border-radius:10px;padding:1px 6px;margin-left:2px">{{ $swipeHistory->count() }}</span>@endif</button>
</div>
</div>
</div>
{{-- Body --}}
<div class="pr-body">
{{-- DEVAM ET --}}
@if($continueItems->count())
<div class="pr-pane on" id="pane-continue">
<div class="pr-sec-h"><span>Kaldığın Yerden Devam Et</span></div>
<div class="cw-grid">
@foreach($continueItems as $cw)
@php $anime = $cw->anime; @endphp
@if(!$anime) @continue @endif
<a href="{{ route('watch',[$anime->slug,$cw->season_number,$cw->episode_number]) }}" class="cw-card">
<div class="cw-thumb">
@if($anime->cover_image)
<img src="{{ \App\Support\MediaUrl::fromStoragePath($anime->cover_image) }}" alt="">
@else
<div class="cw-thumb-ph"><i class="bi bi-image"></i></div>
@endif
<div class="cw-grad"></div>
<div class="cw-play"><i class="bi bi-play-fill"></i></div>
<div class="cw-progress">
<div class="cw-prog-bar"><div class="cw-prog-fill" style="width:{{ $cw->percent_complete }}%"></div></div>
</div>
<div class="cw-ep">S{{ str_pad($cw->season_number,2,'0',STR_PAD_LEFT) }}E{{ str_pad($cw->episode_number,2,'0',STR_PAD_LEFT) }} · {{ $cw->percent_complete }}%</div>
</div>
<div class="cw-info"><div class="cw-info-title">{{ $anime->title }}</div></div>
</a>
@endforeach
</div>
</div>
@endif
{{-- İZLEME LİSTESİ --}}
<div class="pr-pane {{ !$continueItems->count() ? 'on' : '' }}" id="pane-watchlist">
<div class="pr-sec-h" style="display:flex;align-items:center;justify-content:space-between">
<span>İzleme Listesi</span>
@if(auth()->user()->hasPerk('watchlist_export'))
<div style="display:flex;gap:6px">
<a href="{{ route('watchlist.export', ['format'=>'csv']) }}" style="font-size:.72rem;color:rgba(0,245,255,.7);text-decoration:none;display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border:1px solid rgba(0,245,255,.2);border-radius:6px">
<i class="bi bi-download"></i> CSV
</a>
<a href="{{ route('watchlist.export', ['format'=>'json']) }}" style="font-size:.72rem;color:rgba(0,245,255,.7);text-decoration:none;display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border:1px solid rgba(0,245,255,.2);border-radius:6px">
<i class="bi bi-download"></i> JSON
</a>
</div>
@endif
</div>
@if($watchlistItems->count())
{{-- Filter buttons --}}
<div class="wl-filter">
<button class="wl-filter-btn on" data-wl="all">Tümü</button>
@foreach(\App\Models\Watchlist::STATUSES as $status => $label)
@if(isset($watchlistItems[$status]) && $watchlistItems[$status]->count())
<button class="wl-filter-btn" data-wl="{{ $status }}">{{ $label }} <span style="opacity:.5">({{ $watchlistItems[$status]->count() }})</span></button>
@endif
@endforeach
</div>
@foreach(\App\Models\Watchlist::STATUSES as $status => $label)
@if(isset($watchlistItems[$status]) && $watchlistItems[$status]->count())
<div class="wl-section wl-grid" data-wl="{{ $status }}" style="margin-bottom:24px">
@foreach($watchlistItems[$status] as $wl)
@if(!$wl->anime) @continue @endif
<a href="{{ $wl->anime->detailUrl }}" class="wl-card">
@if($wl->anime->cover_image)
<img src="{{ \App\Support\MediaUrl::fromStoragePath($wl->anime->cover_image) }}" alt="" loading="lazy">
@else
<div class="wl-card-ph"><i class="bi bi-image"></i></div>
@endif
<div class="wl-card-title">{{ $wl->anime->title }}</div>
</a>
@endforeach
</div>
@endif
@endforeach
@else
<div class="pr-empty"><i class="bi bi-bookmark"></i>Liste henüz boş.</div>
@endif
</div>
{{-- AKTİVİTE / HEATMAP --}}
<div class="pr-pane" id="pane-activity">
<div class="pr-sec-h"><span>İzleme Aktivitesi Son 1 Yıl</span></div>
@php
$today = now()->startOfDay();
$start = $today->copy()->subDays(364);
$gridStart= $start->copy()->startOfWeek(\Carbon\Carbon::MONDAY);
$weeks = [];
$cur = $gridStart->copy();
while ($cur->lte($today)) {
$week = [];
for ($d = 0; $d < 7; $d++) {
$key = $cur->format('Y-m-d');
$week[] = ['date'=>$key,'count'=>$heatmapRaw[$key]??0,'past'=>$cur->lte($today)&&$cur->gte($start)];
$cur->addDay();
}
$weeks[] = $week;
}
$maxCount = !empty($heatmapRaw) ? max($heatmapRaw) : 1;
$totalDays = collect($heatmapRaw)->filter()->count();
$totalEpYear= array_sum($heatmapRaw);
@endphp
<div class="heatmap-wrap">
<div class="heatmap-scroll">
<div class="heatmap-grid">
@foreach($weeks as $week)
<div class="hm-col">
@foreach($week as $cell)
@php
$alpha = 0;
$bg = 'rgba(255,255,255,.05)';
if ($cell['past'] && $cell['count'] > 0) {
$intensity = min(1, $cell['count'] / max($maxCount,1));
$alpha = 0.18 + $intensity * 0.82;
$bg = "rgba(0,245,255,{$alpha})";
} elseif (!$cell['past']) {
$bg = 'transparent';
}
@endphp
<div class="hm-cell" title="{{ $cell['past'] ? $cell['date'].': '.($cell['count']?$cell['count'].' bölüm':'boş') : '' }}"
style="background:{{ $bg }}"></div>
@endforeach
</div>
@endforeach
</div>
</div>
<div class="heatmap-legend">
<span>Az</span>
<div class="heatmap-legend-cells">
<div style="width:13px;height:13px;border-radius:3px;background:rgba(255,255,255,.05)"></div>
<div style="width:13px;height:13px;border-radius:3px;background:rgba(0,245,255,.2)"></div>
<div style="width:13px;height:13px;border-radius:3px;background:rgba(0,245,255,.45)"></div>
<div style="width:13px;height:13px;border-radius:3px;background:rgba(0,245,255,.7)"></div>
<div style="width:13px;height:13px;border-radius:3px;background:rgba(0,245,255,1)"></div>
</div>
<span>Çok</span>
</div>
<div class="heatmap-stats">
<span><i class="bi bi-calendar3 me-1"></i>{{ $totalDays }} aktif gün</span>
<span><i class="bi bi-play-circle me-1"></i>{{ $totalEpYear }} bölüm izlendi</span>
</div>
</div>
</div>
{{-- BAŞARIMLAR --}}
@if($allAchievements->count())
<div class="pr-pane" id="pane-achievements">
@php $earnedIds = $achievements->pluck('achievement_id')->toArray(); @endphp
<div class="pr-sec-h"><span>Başarımlar <span style="opacity:.4;font-weight:400">{{ $achievements->count() }}/{{ $allAchievements->count() }}</span></span></div>
<div class="ach-grid">
@foreach($allAchievements->sortByDesc(fn($a)=>in_array($a->id,$earnedIds)) as $ach)
@php $earned = in_array($ach->id, $earnedIds); @endphp
<div class="ach-card {{ $earned ? 'earned' : '' }}" style="{{ $earned ? 'border-color:'.substr($ach->color,0,7).'33' : '' }}">
<div class="ach-icon-wrap" style="background:{{ $earned ? substr($ach->color,0,7).'1a' : 'rgba(255,255,255,.04)' }}">
<i class="bi {{ $ach->icon }}" style="color:{{ $earned ? $ach->color : 'rgba(255,255,255,.2)' }}"></i>
</div>
<div class="ach-body">
<div class="ach-title">{{ $ach->title }}</div>
<div class="ach-desc">{{ $ach->description }}</div>
@if($earned)
@php $ea = $achievements->firstWhere('achievement_id',$ach->id); @endphp
<div class="ach-date" style="color:{{ $ach->color }}">{{ $ea?->earned_at?->format('d.m.Y') }}</div>
@endif
</div>
</div>
@endforeach
</div>
</div>
@endif
{{-- NOTLAR --}}
<div class="pr-pane" id="pane-notes">
<div class="pr-sec-h"><span>Bölüm Notlarım</span></div>
@forelse($episodeNotes as $note)
<div class="note-card">
<div class="note-meta">
@if($note->anime)<a href="{{ $note->anime->detailUrl }}" class="note-anime">{{ $note->anime->title }}</a>@endif
@if($note->episode)<span class="note-ep">{{ $note->episode->episode_number }}. Bölüm</span>@endif
@if($note->timestamp_at)<span class="note-ts"><i class="bi bi-clock"></i> {{ $note->timestamp_label }}</span>@endif
<span class="note-date">{{ $note->created_at->format('d.m.Y') }}</span>
</div>
<div class="note-text">{{ $note->content }}</div>
</div>
@empty
<div class="pr-empty"><i class="bi bi-journal-text"></i>Henüz not almadın.<br><small style="font-size:.78rem;opacity:.6">İzlerken bölüm içindeki not ikonuna tıklayarak not alabilirsin.</small></div>
@endforelse
</div>
{{-- YORUMLAR --}}
{{-- ── Keşfet Geçmişi ──────────────────────────────────────── --}}
<div class="pr-pane" id="pane-swipes">
<div class="pr-sec-h" style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px">
<span>Keşfet Geçmişi</span>
@if($swipeHistory->count())
<div style="display:flex;gap:14px;font-size:.68rem;color:var(--text3)">
<span style="color:var(--accent2)">
<i class="bi bi-heart-fill" style="margin-right:3px"></i>
{{ $swipeHistory->where('direction','like')->count() }} beğeni
</span>
<span>
<i class="bi bi-x-lg" style="margin-right:3px;color:#ff2d7d"></i>
{{ $swipeHistory->where('direction','skip')->count() }} geç
</span>
<a href="{{ route('discover') }}" style="color:var(--accent2);text-decoration:none">
<i class="bi bi-arrow-right" style="margin-right:3px"></i>Devam et
</a>
</div>
@endif
</div>
@if($swipeHistory->isEmpty())
<div class="pr-empty">
<i class="bi bi-hand-index-thumb"></i>
Henüz hiç anime keşfetmedin. <a href="{{ route('discover') }}" style="color:var(--accent2);text-decoration:none">Hemen başla </a>
</div>
@else
{{-- Filter buttons --}}
<div style="display:flex;gap:6px;margin-bottom:16px;flex-wrap:wrap">
<button class="swipe-filter on" data-f="all">Tümü <span class="swipe-f-count">{{ $swipeHistory->count() }}</span></button>
<button class="swipe-filter" data-f="like"><i class="bi bi-heart-fill" style="color:var(--accent2);font-size:.75rem"></i> Beğendiklerim <span class="swipe-f-count">{{ $swipeHistory->where('direction','like')->count() }}</span></button>
<button class="swipe-filter" data-f="skip"><i class="bi bi-x-lg" style="color:#ff2d7d;font-size:.75rem"></i> Geçtiklerim <span class="swipe-f-count">{{ $swipeHistory->where('direction','skip')->count() }}</span></button>
</div>
<div class="swipe-grid" id="swipeGrid">
@foreach($swipeHistory as $sw)
@php $a = $sw->anime; @endphp
<a href="{{ $a->detailUrl }}" class="swipe-item {{ $sw->direction }}" data-dir="{{ $sw->direction }}" title="{{ $a->title }}">
<div class="swipe-poster">
@if($a->coverUrl)
<img src="{{ $a->coverUrl }}" alt="{{ $a->title }}" loading="lazy">
@else
<div class="swipe-poster-ph"><i class="bi bi-film"></i></div>
@endif
<div class="swipe-dir-badge {{ $sw->direction === 'like' ? 'badge-like' : 'badge-skip' }}">
@if($sw->direction === 'like')
<i class="bi bi-heart-fill"></i>
@else
<i class="bi bi-x-lg"></i>
@endif
</div>
</div>
<div class="swipe-info">
<div class="swipe-title">{{ $a->title }}</div>
<div class="swipe-meta">
@if($a->release_year)<span>{{ $a->release_year }}</span>@endif
@if($a->rating)<span style="color:#fbbf24"><i class="bi bi-star-fill" style="font-size:.55rem"></i> {{ number_format($a->rating,1) }}</span>@endif
</div>
</div>
</a>
@endforeach
</div>
@endif
</div>
<div class="pr-pane" id="pane-comments">
<div class="pr-sec-h"><span>Son Yorumlar</span></div>
@if($recentComments->isEmpty())
<div class="pr-empty"><i class="bi bi-chat-dots"></i>Henüz yorum yok.</div>
@else
@foreach($recentComments as $c)
<div class="cm-card">
<div class="cm-top">
<div class="cm-ava">{{ mb_strtoupper(mb_substr($user->name,0,1)) }}</div>
<span class="cm-name">{{ $user->name }}</span>
<span class="cm-time">{{ $c->created_at->diffForHumans() }}</span>
</div>
@if($c->content)<div class="cm-text">{{ $c->content }}</div>@endif
@if($c->gif_url)<img src="{{ $c->gif_url }}" alt="" class="cm-gif">@endif
</div>
@endforeach
@endif
</div>
</div>
@endsection
@push('scripts')
<script>
// Tab switching
document.querySelectorAll('.pr-tab').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.pr-tab').forEach(t => t.classList.remove('on'));
document.querySelectorAll('.pr-pane').forEach(p => p.classList.remove('on'));
btn.classList.add('on');
document.getElementById('pane-' + btn.dataset.pane)?.classList.add('on');
});
});
// Watchlist filter
document.querySelectorAll('.wl-filter-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.wl-filter-btn').forEach(b => b.classList.remove('on'));
btn.classList.add('on');
const target = btn.dataset.wl;
document.querySelectorAll('.wl-section').forEach(sec => {
sec.style.display = (target === 'all' || sec.dataset.wl === target) ? 'grid' : 'none';
});
});
});
// Swipe history filter
document.querySelectorAll('.swipe-filter').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.swipe-filter').forEach(b => b.classList.remove('on'));
btn.classList.add('on');
const f = btn.dataset.f;
document.querySelectorAll('#swipeGrid .swipe-item').forEach(item => {
item.style.display = (f === 'all' || item.dataset.dir === f) ? '' : 'none';
});
});
});
</script>
@endpush
@@ -0,0 +1,529 @@
@extends('frontend.layouts.app')
@section('title', $user->name . ' — Profil — Animexe')
@section('meta_description', ($user->bio ? $user->bio . ' — ' : '') . $user->name . ' Animexe profili.')
@push('styles')
<style>
.pub-hero{
position:relative;overflow:hidden;background:#05050e;
}
.pub-hero-animated{
background:linear-gradient(135deg,#05050e,#0a0520);
}
.pub-hero-bg{
position:absolute;inset:0;
background:
radial-gradient(ellipse 80% 60% at 20% 50%,rgba(0,245,255,.06),transparent),
radial-gradient(ellipse 60% 80% at 80% 30%,rgba(255,45,125,.05),transparent);
}
.pub-hero-inner{
position:relative;z-index:1;max-width:900px;margin:0 auto;
padding:calc(var(--nav-h) + 36px) 24px 0;
}
.pub-top{display:flex;align-items:flex-start;gap:20px;flex-wrap:wrap;margin-bottom:28px}
.pub-top-main{display:flex;align-items:flex-end;gap:20px;flex:1;min-width:0}
.pub-avatar{
width:96px;height:96px;border-radius:24px;flex-shrink:0;
background:linear-gradient(135deg,var(--accent2),var(--accent));
display:flex;align-items:center;justify-content:center;
font-size:2.4rem;font-weight:900;color:#fff;overflow:hidden;
box-shadow:0 0 0 3px rgba(0,245,255,.2),0 16px 48px rgba(0,0,0,.5);
}
.pub-info{flex:1;min-width:0;padding-bottom:4px}
.pub-name{font-size:1.7rem;font-weight:900;color:#fff;line-height:1.1;margin-bottom:4px}
.pub-handle{font-size:.84rem;color:rgba(255,255,255,.35);margin-bottom:10px}
.pub-badges{display:flex;gap:7px;flex-wrap:wrap}
.pbadge{padding:4px 12px;border-radius:20px;font-size:.7rem;font-weight:700;letter-spacing:.05em;display:inline-flex;align-items:center;gap:5px}
.pbadge-admin{background:rgba(255,45,125,.15);border:1px solid rgba(255,45,125,.35);color:var(--accent)}
.pbadge-premium{background:rgba(255,208,0,.12);border:1px solid rgba(255,208,0,.3);color:#ffd700}
.pbadge-member{background:rgba(0,245,255,.07);border:1px solid rgba(0,245,255,.2);color:var(--accent2)}
.pub-stats{
display:flex;gap:6px;flex-wrap:wrap;
padding:20px 0 0;border-top:1px solid rgba(255,255,255,.06);margin-top:8px;
}
.pub-stat{
display:flex;flex-direction:column;align-items:center;
padding:10px 20px;border-radius:12px;
background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);
min-width:80px;flex:1;
}
.pub-stat .v{font-size:1.25rem;font-weight:800;color:#fff;line-height:1}
.pub-stat .l{font-size:.62rem;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,.35);margin-top:4px;font-weight:600}
.pub-actions{display:flex;gap:8px;flex-wrap:wrap;align-self:flex-start;flex-shrink:0}
.pub-btn{
padding:8px 16px;border-radius:10px;font-size:.82rem;font-weight:700;
display:inline-flex;align-items:center;gap:7px;cursor:pointer;
border:none;font-family:inherit;transition:all .2s;text-decoration:none;white-space:nowrap;
}
.pub-btn-follow{background:var(--accent2);color:#05050e}
.pub-btn-follow:hover{background:#00d4de}
.pub-btn-follow.following{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.15);color:rgba(255,255,255,.6)}
.pub-btn-follow.following:hover{background:rgba(255,80,80,.12);border-color:rgba(255,80,80,.3);color:#ff5050}
.pub-btn-msg{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);color:rgba(255,255,255,.6)}
.pub-btn-msg:hover{background:rgba(255,255,255,.12);color:#fff}
.pub-btn-edit{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);color:rgba(255,255,255,.6)}
.pub-btn-edit:hover{background:rgba(255,255,255,.12);color:#fff}
.pub-compat{
display:flex;align-items:center;gap:10px;
background:rgba(0,245,255,.06);border:1px solid rgba(0,245,255,.15);
border-radius:12px;padding:10px 16px;margin-top:14px;
}
.pub-compat-bar{flex:1;height:6px;background:rgba(255,255,255,.08);border-radius:3px;overflow:hidden}
.pub-compat-fill{height:100%;background:linear-gradient(90deg,var(--accent2),var(--accent));border-radius:3px;transition:width .8s ease}
.pub-compat-label{font-size:.75rem;color:var(--accent2);font-weight:700;white-space:nowrap}
.pub-tabs{
display:flex;gap:2px;border-top:1px solid rgba(255,255,255,.06);
margin-top:20px;overflow-x:auto;scrollbar-width:none;
}
.pub-tabs::-webkit-scrollbar{display:none}
.pub-tab{
padding:12px 18px;background:none;border:none;
border-bottom:2px solid transparent;
color:rgba(255,255,255,.35);font-size:.82rem;font-weight:600;
cursor:pointer;font-family:inherit;transition:all .15s;
white-space:nowrap;flex-shrink:0;display:flex;align-items:center;gap:6px;
margin-bottom:-1px;
}
.pub-tab:hover{color:rgba(255,255,255,.65)}
.pub-tab.on{color:var(--accent2);border-bottom-color:var(--accent2)}
.pub-body{max-width:900px;margin:0 auto;padding:32px 24px 64px}
.pub-pane{display:none}
.pub-pane.on{display:block}
.pub-sec-h{
font-size:.68rem;text-transform:uppercase;letter-spacing:.12em;
color:rgba(255,255,255,.3);font-weight:700;
margin-bottom:16px;display:flex;align-items:center;gap:8px;
}
.pub-sec-h::before{content:'';width:3px;height:14px;background:linear-gradient(180deg,var(--accent2),var(--accent));border-radius:2px}
.pub-wl-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:10px}
.pub-wl-card{
border-radius:9px;overflow:hidden;text-decoration:none;display:block;
background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);
transition:transform .15s,border-color .15s;
}
.pub-wl-card:hover{transform:translateY(-2px);border-color:rgba(255,255,255,.18)}
.pub-wl-card img{width:100%;aspect-ratio:2/3;object-fit:cover;display:block}
.pub-wl-card-ph{aspect-ratio:2/3;background:rgba(255,255,255,.04);display:flex;align-items:center;justify-content:center;color:rgba(255,255,255,.2)}
.pub-wl-card-title{padding:7px 8px;font-size:.72rem;font-weight:600;color:rgba(255,255,255,.7);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pub-ach-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px}
.pub-ach-card{
display:flex;align-items:center;gap:14px;
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);
border-radius:12px;padding:14px 16px;
}
.pub-ach-icon{width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:1.4rem;flex-shrink:0}
.pub-ach-title{font-size:.85rem;font-weight:700;color:#fff;margin-bottom:2px}
.pub-ach-desc{font-size:.72rem;color:rgba(255,255,255,.35);line-height:1.4}
.pub-comment-card{
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);
border-radius:12px;padding:14px 16px;margin-bottom:10px;
}
.pub-cm-top{display:flex;align-items:center;gap:10px;margin-bottom:8px}
.pub-cm-anime{font-size:.78rem;font-weight:600;color:var(--accent2);text-decoration:none}
.pub-cm-anime:hover{text-decoration:underline}
.pub-cm-time{font-size:.7rem;color:rgba(255,255,255,.25);margin-left:auto}
.pub-cm-text{font-size:.87rem;color:rgba(255,255,255,.6);line-height:1.6}
.pub-empty{text-align:center;padding:48px 20px;color:rgba(255,255,255,.2);font-size:.9rem}
.pub-empty i{font-size:2.5rem;display:block;margin-bottom:12px;opacity:.25}
@media(max-width:600px){
.pub-hero-inner{padding:calc(var(--nav-h) + 16px) 16px 0}
.pub-body{padding:24px 16px 48px}
.pub-avatar{width:72px;height:72px;border-radius:18px;font-size:1.8rem}
.pub-name{font-size:1.3rem}
.pub-top{flex-direction:column;gap:14px}
.pub-ach-grid{grid-template-columns:1fr}
}
</style>
@endpush
@section('content')
@php
$pColor = $user->profile_color ?? '#00f5ff';
$profBg = ($user->profile_bg && $user->hasPerk('profile_bg')) ? $user->profile_bg : null;
$animBanner = $user->hasPerk('animated_banner') && $user->animated_banner;
$watchRank = $user->watchRank();
$musicUrl = ($user->profile_music_url && $user->hasPerk('profile_music')) ? $user->profile_music_url : null;
@endphp
<div class="pub-hero {{ $profBg ? 'profile-bg-'.$profBg : '' }} {{ $animBanner ? 'pub-hero-animated' : '' }}" style="--pr-color:{{ $pColor }}">
@if($animBanner)
<canvas id="banner-canvas" style="position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0"></canvas>
@endif
@if($user->banner_image)
<div style="position:absolute;inset:0;overflow:hidden;z-index:0">
<img src="{{ \App\Support\MediaUrl::fromStoragePath($user->banner_image) }}" alt=""
style="width:100%;height:100%;object-fit:cover;opacity:.35;filter:blur(1px)">
<div style="position:absolute;inset:0;background:linear-gradient(to bottom,rgba(5,5,14,.3) 0%,rgba(5,5,14,.85) 100%)"></div>
</div>
@else
<div class="pub-hero-bg" style="background:radial-gradient(ellipse 80% 60% at 20% 50%,{{ $pColor }}18,transparent),radial-gradient(ellipse 60% 80% at 80% 30%,{{ $pColor }}10,transparent)"></div>
@endif
<div class="pub-hero-inner">
<div class="pub-top">
<div class="pub-top-main">
@php
$hasFrame = $user->profile_frame && $user->hasPerk('profile_frame');
$avatarSrc = $user->gif_avatar && $user->hasPerk('gif_avatar')
? $user->gif_avatar
: ($user->avatar ? \App\Support\MediaUrl::fromStoragePath($user->avatar) : null);
@endphp
<div class="{{ $hasFrame ? 'avatar-wrap' : '' }}" style="{{ $hasFrame ? 'width:96px;height:96px' : '' }}">
@if($hasFrame)
<div class="avatar-frame frame-{{ $user->profile_frame }}"></div>
@endif
<div class="pub-avatar" style="{{ $hasFrame ? 'border-radius:50%;position:relative;z-index:3' : 'background:linear-gradient(135deg,'.$pColor.','.$pColor.'88);box-shadow:0 0 0 3px '.$pColor.'33,0 16px 48px rgba(0,0,0,.5)' }}">
@if($avatarSrc)
<img src="{{ $avatarSrc }}" alt="{{ $user->name }}"
style="width:100%;height:100%;object-fit:cover;border-radius:inherit">
@else
{{ mb_strtoupper(mb_substr($user->name,0,1)) }}
@endif
</div>
</div>
<div class="pub-info">
@php
$unColor = $user->username_color && $user->hasPerk('username_color') ? 'username-color-'.$user->username_color : '';
$unEffect = $user->username_effect && $user->hasPerk('username_effect') ? 'username-effect-'.$user->username_effect : '';
@endphp
<div class="pub-name {{ $unColor }} {{ $unEffect }}"
@if($user->username_effect==='glitch' && $unEffect) data-text="{{ $user->name }}" @endif>{{ $user->name }}</div>
@if($user->username)
<div class="pub-handle">@php echo '@' . e($user->username); @endphp</div>
@endif
@if($user->bio)
<div style="font-size:.83rem;color:rgba(255,255,255,.55);margin-bottom:10px;max-width:500px;line-height:1.5">{{ $user->bio }}</div>
@endif
<div class="pub-badges">
@if($user->role==='admin')
<span class="pbadge pbadge-admin"><i class="bi bi-shield-fill"></i> Admin</span>
@elseif($user->role==='moderator')
<span class="pbadge pbadge-admin"><i class="bi bi-shield-half"></i> Moderatör</span>
@endif
@if($user->isPremium())
<span class="pbadge pbadge-premium"><i class="bi bi-star-fill"></i> Premium</span>
@else
<span class="pbadge pbadge-member"><i class="bi bi-person-check"></i> Üye</span>
@endif
@if($user->profile_badge && $user->hasPerk('profile_badge'))
<span class="premium-badge user-badge-custom">{{ $user->profile_badge }}</span>
@endif
@if($watchRank)
<span class="pbadge" style="background:rgba(255,255,255,.06);border:1px solid {{ $watchRank['color'] }}44;color:{{ $watchRank['color'] }}">
<i class="bi {{ $watchRank['icon'] }}"></i> {{ $watchRank['label'] }}
</span>
@endif
<span class="pbadge" style="background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.1);color:rgba(255,255,255,.35)"><i class="bi bi-calendar3"></i> {{ $user->created_at->format('Y') }}'den beri</span>
</div>
</div>
</div>
<div class="pub-actions">
@if($isOwnProfile)
<a href="{{ route('profile.settings') }}" class="pub-btn pub-btn-edit">
<i class="bi bi-pencil-square"></i> Profili Düzenle
</a>
@elseif(auth()->check())
<button id="follow-btn" onclick="toggleFollow()"
class="pub-btn pub-btn-follow {{ $isFollowing ? 'following' : '' }}"
data-following="{{ $isFollowing ? '1' : '0' }}">
<i class="bi {{ $isFollowing ? 'bi-person-check-fill' : 'bi-person-plus' }}" id="follow-icon"></i>
<span id="follow-label">{{ $isFollowing ? 'Takip Ediliyor' : 'Takip Et' }}</span>
</button>
<form method="POST" action="{{ route('messages.start', $user) }}">
@csrf
<button type="submit" class="pub-btn pub-btn-msg">
<i class="bi bi-chat-dots"></i> Mesaj
</button>
</form>
@endif
</div>
</div>
@if($compatibility !== null)
<div class="pub-compat">
<i class="bi bi-heart-pulse" style="color:var(--accent2);font-size:1rem;flex-shrink:0"></i>
<div style="flex:1;min-width:0">
<div style="font-size:.72rem;color:rgba(255,255,255,.4);margin-bottom:5px">Anime zevk uyumu</div>
<div class="pub-compat-bar"><div class="pub-compat-fill" id="compat-fill" style="width:0%"></div></div>
</div>
<span class="pub-compat-label" id="compat-pct">%{{ $compatibility }}</span>
</div>
@endif
<div class="pub-stats">
<div class="pub-stat"><span class="v">{{ $watchStats['episodes'] }}</span><span class="l">Bölüm</span></div>
<div class="pub-stat"><span class="v">{{ $watchStats['hours'] }}</span><span class="l">Saat</span></div>
<div class="pub-stat"><span class="v" id="follower-count">{{ $followerCount }}</span><span class="l">Takipçi</span></div>
<div class="pub-stat"><span class="v">{{ $followingCount }}</span><span class="l">Takip</span></div>
<div class="pub-stat"><span class="v">{{ $commentCount }}</span><span class="l">Yorum</span></div>
</div>
<div class="pub-tabs">
<button class="pub-tab on" onclick="switchTab('watchlist',this)"><i class="bi bi-bookmark"></i> İzleme Listesi</button>
<button class="pub-tab" onclick="switchTab('achievements',this)"><i class="bi bi-trophy"></i> Başarımlar</button>
<button class="pub-tab" onclick="switchTab('comments',this)"><i class="bi bi-chat-dots"></i> Yorumlar</button>
</div>
</div>
</div>
@if($musicUrl)
<div id="profile-music-bar" style="
position:fixed;bottom:0;left:0;right:0;z-index:900;
background:rgba(5,5,14,.92);backdrop-filter:blur(12px);
border-top:1px solid rgba(0,245,255,.15);
display:flex;align-items:center;gap:12px;padding:10px 20px;
font-size:.82rem;
">
<button id="music-toggle" onclick="toggleMusic()" style="
background:rgba(0,245,255,.12);border:1px solid rgba(0,245,255,.3);color:#00f5ff;
border-radius:50%;width:36px;height:36px;display:flex;align-items:center;justify-content:center;
cursor:pointer;flex-shrink:0;font-size:1rem;
"><i class="bi bi-play-fill" id="music-icon"></i></button>
<div style="flex:1;min-width:0">
<div style="color:rgba(255,255,255,.5);font-size:.7rem;margin-bottom:2px">{{ $user->name }} · Profil Müziği</div>
<div id="music-progress-bar" style="width:100%;height:3px;background:rgba(255,255,255,.1);border-radius:2px;overflow:hidden">
<div id="music-progress-fill" style="width:0%;height:100%;background:linear-gradient(90deg,#00f5ff,#b84dff);transition:width .5s linear;border-radius:2px"></div>
</div>
</div>
<button onclick="document.getElementById('profile-music-bar').remove()" style="
background:none;border:none;color:rgba(255,255,255,.3);cursor:pointer;font-size:1.1rem;padding:4px;
"><i class="bi bi-x"></i></button>
<audio id="profile-audio" src="" preload="none"></audio>
</div>
@endif
<div class="pub-body">
{{-- İzleme Listesi --}}
<div class="pub-pane on" id="pane-watchlist">
@php $watching = $watchlistItems->get('watching',[]);
$completed = $watchlistItems->get('completed',[]);
$planToWatch = $watchlistItems->get('plan_to_watch',[]); @endphp
@if($watchlistItems->isEmpty())
<div class="pub-empty"><i class="bi bi-bookmark"></i> Henüz izleme listesi yok.</div>
@else
@foreach(['watching'=>'İzliyor','completed'=>'Tamamladı','plan_to_watch'=>'İzleyecek'] as $status => $label)
@php $items = $watchlistItems->get($status, collect()); @endphp
@if($items->count())
<div class="pub-sec-h">{{ $label }} <span style="color:rgba(255,255,255,.2)">({{ $items->count() }})</span></div>
<div class="pub-wl-grid" style="margin-bottom:28px">
@foreach($items->take(12) as $wl)
@if($wl->anime)
<a href="{{ $wl->anime->detailUrl }}" class="pub-wl-card">
@if($wl->anime->cover_image)
<img src="{{ \App\Support\MediaUrl::fromStoragePath($wl->anime->cover_image) }}" alt="{{ $wl->anime->title }}" loading="lazy">
@else
<div class="pub-wl-card-ph"><i class="bi bi-image"></i></div>
@endif
<div class="pub-wl-card-title">{{ $wl->anime->title }}</div>
</a>
@endif
@endforeach
</div>
@endif
@endforeach
@endif
</div>
{{-- Başarımlar --}}
<div class="pub-pane" id="pane-achievements">
@if($achievements->isEmpty())
<div class="pub-empty"><i class="bi bi-trophy"></i> Henüz başarım kazanılmamış.</div>
@else
<div class="pub-ach-grid">
@foreach($achievements as $ua)
<div class="pub-ach-card">
<div class="pub-ach-icon" style="background:{{ substr($ua->achievement->color ?? '#ffffff',0,7) }}22">
<i class="bi {{ $ua->achievement->icon ?? 'bi-trophy' }}" style="color:{{ $ua->achievement->color ?? '#ffffff' }}"></i>
</div>
<div>
<div class="pub-ach-title">{{ $ua->achievement->name }}</div>
<div class="pub-ach-desc">{{ $ua->achievement->description }}</div>
</div>
</div>
@endforeach
</div>
@endif
</div>
{{-- Yorumlar --}}
<div class="pub-pane" id="pane-comments">
@if($recentComments->isEmpty())
<div class="pub-empty"><i class="bi bi-chat-dots"></i> Henüz yorum yok.</div>
@else
@foreach($recentComments as $c)
@php $ep = $c->episode; $anime = $ep?->anime ?? $c->anime; @endphp
<div class="pub-comment-card">
<div class="pub-cm-top">
@if($anime)
<a href="{{ $anime->detailUrl }}" class="pub-cm-anime"><i class="bi bi-play-circle"></i> {{ $anime->title }}</a>
@endif
<span class="pub-cm-time">{{ $c->created_at->diffForHumans() }}</span>
</div>
<div class="pub-cm-text">{{ Str::limit($c->content, 200) }}</div>
</div>
@endforeach
@endif
</div>
</div>
@endsection
@push('scripts')
<script>
function switchTab(name, btn) {
document.querySelectorAll('.pub-pane').forEach(p => p.classList.remove('on'));
document.querySelectorAll('.pub-tab').forEach(t => t.classList.remove('on'));
document.getElementById('pane-' + name).classList.add('on');
btn.classList.add('on');
}
// Uyum barı animasyonu
document.addEventListener('DOMContentLoaded', () => {
const fill = document.getElementById('compat-fill');
if (fill) setTimeout(() => fill.style.width = '{{ $compatibility ?? 0 }}%', 300);
});
// ── Animasyonlu Banner ────────────────────────────────────────────────────
@if($animBanner)
(function(){
const canvas = document.getElementById('banner-canvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const color = '{{ $pColor }}';
let W, H, particles = [];
function resize(){
W = canvas.width = canvas.offsetWidth;
H = canvas.height = canvas.offsetHeight;
}
resize();
window.addEventListener('resize', resize);
for (let i = 0; i < 55; i++) {
particles.push({
x: Math.random()*1000, y: Math.random()*300,
r: Math.random()*2+.5, vx: (Math.random()-.5)*.4,
vy: (Math.random()-.5)*.4, a: Math.random()*.6+.1
});
}
function hex2rgb(h){ const r=parseInt(h.slice(1,3),16),g=parseInt(h.slice(3,5),16),b=parseInt(h.slice(5,7),16); return `${r},${g},${b}`; }
const rgb = hex2rgb(color.length===7 ? color : '#00f5ff');
function draw(){
ctx.clearRect(0,0,W,H);
particles.forEach(p => {
p.x += p.vx; p.y += p.vy;
if (p.x<0) p.x=W; if (p.x>W) p.x=0;
if (p.y<0) p.y=H; if (p.y>H) p.y=0;
ctx.beginPath();
ctx.arc(p.x,p.y,p.r,0,Math.PI*2);
ctx.fillStyle = `rgba(${rgb},${p.a})`;
ctx.fill();
});
// connecting lines
particles.forEach((a,i) => {
particles.slice(i+1).forEach(b => {
const d = Math.hypot(a.x-b.x,a.y-b.y);
if (d < 90) {
ctx.beginPath();
ctx.moveTo(a.x,a.y); ctx.lineTo(b.x,b.y);
ctx.strokeStyle = `rgba(${rgb},${(1-d/90)*.12})`;
ctx.lineWidth = .6;
ctx.stroke();
}
});
});
requestAnimationFrame(draw);
}
draw();
})();
@endif
// ── Profil Müziği ─────────────────────────────────────────────────────────
@if($musicUrl)
(function(){
const audio = document.getElementById('profile-audio');
const icon = document.getElementById('music-icon');
const fill = document.getElementById('music-progress-fill');
if (!audio) return;
const src = {{ json_encode($musicUrl) }};
// YouTube/SoundCloud embedding via iframe is complex; for direct audio URLs autoplay
if (src.match(/\.(mp3|ogg|wav|m4a|aac|flac)(\?|$)/i)) {
audio.src = src;
audio.volume = 0.5;
} else {
// Not a direct audio file — show the bar with a link instead
const bar = document.getElementById('profile-music-bar');
if (bar) {
const btn = document.getElementById('music-toggle');
btn.innerHTML = '<i class="bi bi-box-arrow-up-right"></i>';
btn.onclick = () => window.open(src, '_blank');
fill.style.width = '100%';
}
return;
}
audio.addEventListener('timeupdate', () => {
if (audio.duration) fill.style.width = (audio.currentTime/audio.duration*100)+'%';
});
audio.addEventListener('ended', () => {
icon.className = 'bi bi-play-fill';
fill.style.width = '0%';
});
window.toggleMusic = function(){
if (audio.paused) { audio.play(); icon.className = 'bi bi-pause-fill'; }
else { audio.pause(); icon.className = 'bi bi-play-fill'; }
};
})();
@else
window.toggleMusic = function(){};
@endif
// Takip toggle
const FOLLOW_URL = '{{ route("user.follow", $user) }}';
const CSRF = document.querySelector('meta[name=csrf-token]')?.content || '';
async function toggleFollow() {
const btn = document.getElementById('follow-btn');
if (!btn || btn.disabled) return;
btn.disabled = true;
try {
const res = await fetch(FOLLOW_URL, {
method: 'POST',
headers: { 'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json' }
});
const d = await res.json();
const icon = document.getElementById('follow-icon');
const label = document.getElementById('follow-label');
const cnt = document.getElementById('follower-count');
if (d.following) {
btn.classList.add('following');
icon.className = 'bi bi-person-check-fill';
label.textContent = 'Takip Ediliyor';
} else {
btn.classList.remove('following');
icon.className = 'bi bi-person-plus';
label.textContent = 'Takip Et';
}
if (cnt) cnt.textContent = d.followers_count;
} catch(e) {}
btn.disabled = false;
}
</script>
@endpush
+229
View File
@@ -0,0 +1,229 @@
@extends('frontend.layouts.app')
@section('title', ($q ? '"'.$q.'" — Arama' : 'Tüm Animeler') . ' — Animexe')
@section('meta_description', $q ? '"' . $q . '" için anime arama sonuçları — Animexe\'de Türkçe anime izleyin.' : 'Animexe\'de tüm anime dizi ve filmlerini keşfedin. Tür, durum ve yıla göre filtreleyin.')
@section('robots', ($q || request()->has('genre') || request()->has('type') || request()->has('year') || $results->currentPage() > 1) ? 'noindex, follow' : 'index, follow')
@push('styles')
<style>
/* ═══════════════════════════════════════════════════
SEARCH PAGE PREMIUM
═══════════════════════════════════════════════════ */
.srch-hero{
background:linear-gradient(180deg,rgba(8,8,20,.98) 0%,rgba(6,6,13,.99) 100%);
padding:clamp(28px,5vw,48px) clamp(16px,4vw,48px) clamp(24px,3.5vw,36px);
border-bottom:1px solid rgba(0,245,255,.1);
position:relative;overflow:hidden;
}
.srch-hero::before{
content:'';position:absolute;top:-60%;left:-10%;right:-10%;bottom:-20%;
background:
radial-gradient(ellipse 60% 50% at 30% 50%,rgba(0,245,255,.05),transparent),
radial-gradient(ellipse 50% 40% at 70% 50%,rgba(255,45,125,.04),transparent);
pointer-events:none;
}
.srch-hero::after{
content:'';position:absolute;bottom:0;left:5%;right:5%;height:1px;
background:linear-gradient(90deg,transparent,var(--accent2),var(--accent),transparent);
opacity:.4;
}
.srch-hero h1{
font-family:var(--font-display);
font-size:clamp(1.3rem,3vw,2rem);
font-weight:900;margin-bottom:22px;letter-spacing:.05em;text-transform:uppercase;
background:linear-gradient(135deg,#fff 0%,var(--accent2) 55%,var(--accent) 100%);
-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;
animation:srch-in .5s cubic-bezier(.22,1,.36,1) both;
}
@keyframes srch-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
.srch-form{
display:flex;flex-direction:column;gap:10px;max-width:1100px;
animation:srch-in .5s cubic-bezier(.22,1,.36,1) .1s both;
}
.srch-row{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.srch-inp-wrap{position:relative;flex:1 1 260px;min-width:0}
.srch-inp-wrap i{position:absolute;left:13px;top:50%;transform:translateY(-50%);color:var(--accent2);font-size:.9rem;pointer-events:none;opacity:.8}
.srch-inp-wrap input{
width:100%;padding:13px 16px 13px 44px;
background:rgba(17,17,31,.92);border:1px solid rgba(0,245,255,.22);
border-radius:12px;color:var(--text);font-size:.94rem;outline:none;
transition:border .18s,box-shadow .18s,background .18s;
}
.srch-inp-wrap input:focus{
border-color:var(--accent2);
box-shadow:0 0 0 3px rgba(0,245,255,.14),0 0 28px rgba(0,245,255,.08);
background:rgba(0,245,255,.05);
}
.srch-inp-wrap input::placeholder{color:var(--text3)}
.srch-sel{
padding:11px 10px;
background:#11111f;border:1px solid rgba(0,245,255,.15);
border-radius:10px;color:var(--text);font-size:.82rem;outline:none;cursor:pointer;
flex:1 1 120px;transition:border .18s;
-webkit-appearance:none;appearance:none;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%2300f5ff' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat:no-repeat;
background-position:right 10px center;
padding-right:30px;
}
.srch-sel option{background:#11111f;color:var(--text)}
.srch-sel:focus{border-color:var(--accent2);box-shadow:0 0 0 2px rgba(0,245,255,.1)}
.srch-sel-sort{
border-color:rgba(255,45,125,.4);
background:#1a0d14;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23ff2d7d' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat:no-repeat;
background-position:right 10px center;
font-weight:700;
color:#ff8ab0;
}
.srch-sel-sort option{background:#1a0d14;color:#fff}
.srch-sel-sort:focus{border-color:var(--accent3)}
.srch-btn{
padding:11px 22px;border-radius:10px;border:none;
background:linear-gradient(135deg,var(--accent),var(--accent3));
color:#fff;font-size:.88rem;font-weight:800;cursor:pointer;
transition:transform .14s,box-shadow .18s;flex-shrink:0;
box-shadow:0 0 24px rgba(255,45,125,.3),0 4px 16px rgba(0,0,0,.3);
}
.srch-btn:hover{transform:translateY(-1px);box-shadow:0 0 32px rgba(255,45,125,.45);opacity:1}
.srch-sort-label{
font-size:.72rem;font-weight:700;color:var(--text3);letter-spacing:.06em;
text-transform:uppercase;white-space:nowrap;padding-left:2px;
}
.srch-count{
color:var(--text2);font-size:.85rem;margin-bottom:16px;
animation:srch-in .4s cubic-bezier(.22,1,.36,1) .15s both;
}
.srch-count strong{color:var(--accent2)}
/* Card grid reveal animation */
.a-grid .a-card{
animation:srch-card-in .45s cubic-bezier(.22,1,.36,1) both;
}
@keyframes srch-card-in{
from{opacity:0;transform:translateY(16px) scale(.97)}
to{opacity:1;transform:none}
}
@media(max-width:600px){
.srch-hero{padding:24px 14px 20px}
.srch-hero h1{font-size:1.05rem;margin-bottom:12px}
.srch-form{gap:8px}
.srch-sel{font-size:.8rem;padding:9px 8px;flex:1 1 90px}
.srch-btn{width:100%;justify-content:center;padding:11px;display:flex;align-items:center;gap:6px}
.srch-sort-label{display:none}
}
@media(max-width:380px){
.srch-sel{flex:1 1 70px;font-size:.75rem;padding:8px 5px}
}
</style>
@endpush
@section('content')
<div class="srch-hero">
<div style="max-width:1600px;margin:0 auto">
<h1>{{ $q ? '"'.$q.'" için sonuçlar' : 'Tüm Animeler' }}</h1>
<form action="{{ route('search') }}" method="GET" class="srch-form">
{{-- Satır 1: Arama kutusu + Ara butonu --}}
<div class="srch-row">
<div class="srch-inp-wrap">
<i class="bi bi-search"></i>
<input type="text" name="q" value="{{ $q }}" placeholder="Anime adı, alternatif isim...">
</div>
<button type="submit" class="srch-btn"><i class="bi bi-search"></i> Ara</button>
</div>
{{-- Satır 2: Filtreler + Sıralama --}}
<div class="srch-row">
<select name="type" class="srch-sel">
<option value="">Format (Hepsi)</option>
<option value="series" {{ $type==='series' ?'selected':'' }}>Dizi</option>
<option value="movie" {{ $type==='movie' ?'selected':'' }}>Film</option>
<option value="ova" {{ $type==='ova' ?'selected':'' }}>OVA</option>
<option value="ona" {{ $type==='ona' ?'selected':'' }}>ONA</option>
<option value="special" {{ $type==='special'?'selected':'' }}>Special</option>
</select>
<select name="status" class="srch-sel">
<option value="">Durum (Hepsi)</option>
<option value="ongoing" {{ $status==='ongoing' ?'selected':'' }}>Devam Ediyor</option>
<option value="completed"{{ $status==='completed'?'selected':'' }}>Tamamlandı</option>
<option value="upcoming" {{ $status==='upcoming' ?'selected':'' }}>Yakında</option>
</select>
<select name="genre" class="srch-sel">
<option value="">Tür (Hepsi)</option>
@foreach($genres as $g)
<option value="{{ $g->slug }}" {{ $genre===$g->slug?'selected':'' }}>{{ $g->name }}</option>
@endforeach
</select>
<select name="year" class="srch-sel">
<option value="">Yıl (Hepsi)</option>
@foreach($years as $y)
<option value="{{ $y }}" {{ (string)$year===(string)$y?'selected':'' }}>{{ $y }}</option>
@endforeach
</select>
<span class="srch-sort-label"><i class="bi bi-sort-down"></i> Sırala:</span>
<select name="sort" class="srch-sel srch-sel-sort" onchange="this.form.submit()">
<option value="popular" {{ $sort==='popular' ?'selected':'' }}>🔥 En Popüler</option>
<option value="rating" {{ $sort==='rating' ?'selected':'' }}> En Yüksek Puan</option>
<option value="newest" {{ $sort==='newest' ?'selected':'' }}>🆕 En Yeni</option>
<option value="oldest" {{ $sort==='oldest' ?'selected':'' }}>📅 En Eski</option>
<option value="az" {{ $sort==='az' ?'selected':'' }}>🔤 A Z</option>
<option value="za" {{ $sort==='za' ?'selected':'' }}>🔤 Z A</option>
@auth
<option value="personalized" {{ $sort==='personalized' ?'selected':'' }}> Sana Özel</option>
@endauth
</select>
</div>
</form>
</div>
</div>
<div class="section">
@php
$sortLabels = [
'popular' => '🔥 En Popüler',
'rating' => '⭐ En Yüksek Puan',
'newest' => '🆕 En Yeni',
'oldest' => '📅 En Eski',
'az' => '🔤 A → Z',
'za' => '🔤 Z → A',
'personalized'=> '✨ Sana Özel',
];
@endphp
<p class="srch-count">
Toplam <strong>{{ $results->total() }}</strong> anime bulundu
<span style="margin-left:10px;font-size:.78rem;color:var(--text3)">· {{ $sortLabels[$sort] ?? '' }}</span>
</p>
@if($results->count())
<div class="a-grid">
@foreach($results as $anime)
<a href="{{ $anime->slug ? route('anime.show', $anime->slug) : '#' }}" class="a-card">
<div class="a-poster">
@if($anime->coverUrl)
<img src="{{ $anime->coverUrl }}" alt="{{ $anime->title }}" loading="lazy">
@else
<div class="a-no-img"><i class="bi bi-image"></i></div>
@endif
@if($anime->type==='movie')<span class="a-badge">Film</span>@endif
@if($anime->rating)<span class="a-rating"><i class="bi bi-star-fill" style="font-size:.65rem"></i>{{ number_format($anime->rating,1) }}</span>@endif
<div class="a-hover-play"><div class="play-circle"><i class="bi bi-play-fill"></i></div></div>
</div>
<div class="a-info">
<div class="a-title">{{ $anime->title }}</div>
<div class="a-meta">{{ $anime->release_year }}@if($anime->episode_count) · {{ $anime->episode_count }}@endif</div>
</div>
</a>
@endforeach
</div>
@if($results->hasPages())
<div style="margin-top:32px">{{ $results->links('frontend.pagination') }}</div>
@endif
@else
<div style="text-align:center;padding:64px 20px;color:var(--text3)">
<i class="bi bi-search" style="font-size:3rem;opacity:.25;display:block;margin-bottom:14px"></i>
<p>Sonuç bulunamadı. Farklı bir arama terimi deneyin.</p>
<a href="{{ route('search') }}" style="color:var(--accent);margin-top:12px;display:inline-block">Tümünü Göster</a>
</div>
@endif
</div>
@endsection
@@ -0,0 +1,222 @@
@extends('frontend.layouts.app')
@section('title', 'Anime Mahkemesi | Animexe')
@push('styles')
<style>
.trib-hero{padding-top:calc(var(--nav-h) + 40px);padding-bottom:32px;text-align:center;background:radial-gradient(ellipse at 50% 0%,rgba(255,45,125,.12) 0%,transparent 70%)}
.trib-hero h1{font-size:2rem;font-weight:900;letter-spacing:-.02em;margin-bottom:8px}
.trib-hero h1 span{color:#ff2d7d}
.trib-hero p{color:rgba(255,255,255,.45);font-size:.95rem}
.trib-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:16px;padding:0 0 40px}
.trib-card{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);border-radius:14px;padding:20px;transition:border-color .2s,transform .2s;cursor:pointer;text-decoration:none;display:block;color:#fff}
.trib-card:hover{border-color:rgba(255,45,125,.3);transform:translateY(-2px)}
.trib-card-anime{font-size:.72rem;color:rgba(255,45,125,.8);font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px}
.trib-card-q{font-size:.98rem;font-weight:700;line-height:1.4;margin-bottom:14px}
.trib-sides{display:flex;gap:8px;margin-bottom:14px}
.trib-side{flex:1;padding:6px 10px;border-radius:8px;font-size:.75rem;font-weight:700;text-align:center}
.trib-side.a{background:rgba(0,200,100,.1);color:#00c864;border:1px solid rgba(0,200,100,.2)}
.trib-side.b{background:rgba(255,60,60,.1);color:#ff3c3c;border:1px solid rgba(255,60,60,.2)}
.trib-side.c{background:rgba(59,130,246,.1);color:#3b82f6;border:1px solid rgba(59,130,246,.2)}
.trib-side.d{background:rgba(245,158,11,.1);color:#f59e0b;border:1px solid rgba(245,158,11,.2)}
.trib-side.e{background:rgba(167,139,250,.1);color:#a78bfa;border:1px solid rgba(167,139,250,.2)}
.trib-side.f{background:rgba(236,72,153,.1);color:#ec4899;border:1px solid rgba(236,72,153,.2)}
.trib-meta{display:flex;gap:12px;font-size:.72rem;color:rgba(255,255,255,.3)}
.trib-status-open{color:#00c864;font-weight:700}
.trib-status-closed{color:rgba(255,255,255,.3)}
.trib-create-btn{display:inline-flex;align-items:center;gap:8px;padding:10px 24px;border-radius:20px;background:rgba(255,45,125,.15);border:1px solid rgba(255,45,125,.3);color:#ff2d7d;font-weight:700;font-size:.88rem;cursor:pointer;text-decoration:none;transition:all .15s;font-family:inherit}
.trib-create-btn:hover{background:rgba(255,45,125,.28)}
/* Modal */
.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:1000;display:none;align-items:center;justify-content:center;padding:16px}
.modal-overlay.on{display:flex}
.modal-box{background:#0e0e1a;border:1px solid rgba(255,255,255,.1);border-radius:18px;padding:28px;width:100%;max-width:520px;max-height:90vh;overflow-y:auto}
.modal-title{font-size:1.15rem;font-weight:900;margin-bottom:20px}
.mf-label{font-size:.75rem;font-weight:700;color:rgba(255,255,255,.45);text-transform:uppercase;letter-spacing:.07em;margin-bottom:6px}
.mf-input{width:100%;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:10px 14px;color:#fff;font-size:.9rem;font-family:inherit;outline:none;margin-bottom:14px;resize:none}
.mf-row{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.mf-btn{width:100%;padding:12px;border-radius:12px;border:none;background:rgba(255,45,125,.2);color:#ff2d7d;font-weight:700;font-size:.9rem;cursor:pointer;font-family:inherit;transition:background .15s}
.mf-btn:hover{background:rgba(255,45,125,.35)}
</style>
@endpush
@section('content')
<div style="max-width:1200px;margin:0 auto;padding:0 20px">
<div class="trib-hero">
<h1>⚖️ Anime <span>Mahkemesi</span></h1>
<p>Tartışmalı kararlar, karakterler, sahneler topluluk yargılıyor.</p>
@auth
<br>
<button class="trib-create-btn" onclick="document.getElementById('create-modal').classList.add('on')">
<i class="bi bi-plus-lg"></i> Dava
</button>
@endauth
</div>
<div class="trib-grid">
@forelse($tribunals as $t)
<a class="trib-card" href="{{ route('tribunal.show', $t) }}">
<div class="trib-card-anime">{{ $t->anime->title }}</div>
<div class="trib-card-q">{{ $t->question }}</div>
<div class="trib-sides" style="grid-template-columns:repeat({{ min(count($t->allSides()), 4) }},1fr)">
@foreach($t->allSides() as $key => $label)
<div class="trib-side {{ $key }}">{{ Str::limit($label, 20) }}</div>
@endforeach
</div>
<div class="trib-meta">
<span class="trib-status-{{ $t->status }}">{{ $t->status === 'open' ? '🟢 Açık' : '🔴 Kapalı' }}</span>
<span><i class="bi bi-people"></i> {{ $t->votes_count }} oy</span>
<span>{{ $t->created_at->diffForHumans() }}</span>
</div>
</a>
@empty
<div style="grid-column:1/-1;text-align:center;padding:60px;color:rgba(255,255,255,.3)">
<i class="bi bi-balance" style="font-size:3rem;display:block;margin-bottom:12px"></i>
Henüz dava açılmamış.
</div>
@endforelse
</div>
{{ $tribunals->links('frontend.pagination') }}
</div>
{{-- Dava Açma Modalı --}}
@auth
<div class="modal-overlay" id="create-modal" onclick="if(event.target===this)this.classList.remove('on')">
<div class="modal-box">
<div class="modal-title">⚖️ Yeni Dava </div>
<div class="mf-label">Anime</div>
<input class="mf-input" type="text" id="cm-anime-search" placeholder="Anime adı ara..." autocomplete="off" oninput="searchAnime(this.value)">
<div id="cm-anime-results" style="margin-bottom:14px;display:none;background:rgba(0,0,0,.4);border:1px solid rgba(255,255,255,.1);border-radius:10px;overflow:hidden;max-height:160px;overflow-y:auto"></div>
<input type="hidden" id="cm-anime-id">
<div class="mf-label">Soru / Karar</div>
<textarea class="mf-input" id="cm-question" rows="2" maxlength="280" placeholder="Örn: Sasuke'nin Konoha'yı terk etmesi doğru muydu?"></textarea>
<div id="cm-sides-wrap">
<div class="mf-row" style="margin-bottom:0">
<div>
<div class="mf-label" style="color:#00c864">Taraf A (Yeşil)</div>
<input class="mf-input" id="cm-side-a" maxlength="100" placeholder="Örn: Haklıydı">
</div>
<div>
<div class="mf-label" style="color:#ff3c3c">Taraf B (Kırmızı)</div>
<input class="mf-input" id="cm-side-b" maxlength="100" placeholder="Örn: Haksızdı">
</div>
</div>
<div id="cm-extra-sides"></div>
</div>
<div style="margin-bottom:14px">
<button type="button" id="cm-add-side-btn" onclick="addExtraSide()"
style="background:none;border:1px dashed rgba(255,255,255,.2);border-radius:8px;color:rgba(255,255,255,.4);font-size:.78rem;padding:6px 14px;cursor:pointer;font-family:inherit;transition:all .15s;width:100%"
onmouseover="this.style.borderColor='rgba(255,255,255,.4)';this.style.color='#fff'"
onmouseout="this.style.borderColor='rgba(255,255,255,.2)';this.style.color='rgba(255,255,255,.4)'">
<i class="bi bi-plus-lg"></i> Seçenek Ekle (max 4 ekstra)
</button>
</div>
<div class="mf-label">Kapanış Tarihi (isteğe bağlı)</div>
<input class="mf-input" type="date" id="cm-closes-at" min="{{ now()->addDay()->format('Y-m-d') }}">
<div id="cm-error" style="color:#ff4444;font-size:.8rem;margin-bottom:10px;display:none"></div>
<button class="mf-btn" onclick="submitTribunal()">Davayı </button>
</div>
</div>
@endauth
@endsection
@push('scripts')
<script>
const CSRF = document.querySelector('meta[name="csrf-token"]')?.content ?? '';
let animeSearchTimer = null;
function searchAnime(q) {
clearTimeout(animeSearchTimer);
if (q.length < 2) { document.getElementById('cm-anime-results').style.display = 'none'; return; }
animeSearchTimer = setTimeout(async () => {
const r = await fetch(`/search?q=${encodeURIComponent(q)}&json=1`);
if (!r.ok) return;
const d = await r.json();
const res = document.getElementById('cm-anime-results');
const items = (d.animes || d.results || []).slice(0, 6);
if (!items.length) { res.style.display = 'none'; return; }
res.style.display = 'block';
res.innerHTML = items.map(a => `<div onclick="selectAnime(${a.id},'${a.title.replace(/'/g,"\\'")}')"
style="padding:8px 12px;cursor:pointer;font-size:.85rem;border-bottom:1px solid rgba(255,255,255,.05);display:flex;align-items:center;gap:10px"
onmouseover="this.style.background='rgba(255,255,255,.07)'" onmouseout="this.style.background=''">
${a.cover ? `<img src="${a.cover}" style="width:28px;height:40px;object-fit:cover;border-radius:3px;flex-shrink:0">` : '<div style="width:28px;height:40px;background:#222;border-radius:3px;flex-shrink:0"></div>'}
<span>${a.title}</span>
</div>`).join('');
}, 300);
}
function selectAnime(id, title) {
document.getElementById('cm-anime-id').value = id;
document.getElementById('cm-anime-search').value = title;
document.getElementById('cm-anime-results').style.display = 'none';
}
const SIDE_COLORS = ['#00c864','#ff3c3c','#3b82f6','#f59e0b','#a78bfa','#ec4899'];
const SIDE_NAMES = ['A (Yeşil)','B (Kırmızı)','C (Mavi)','D (Turuncu)','E (Mor)','F (Pembe)'];
let extraSideCount = 0;
function addExtraSide() {
if (extraSideCount >= 4) return;
extraSideCount++;
const idx = extraSideCount + 1; // 0=a,1=b, so extra starts at 2 (c)
const color = SIDE_COLORS[idx] || '#ffffff';
const name = SIDE_NAMES[idx] || ('Seçenek ' + (idx+1));
const wrap = document.getElementById('cm-extra-sides');
const div = document.createElement('div');
div.id = 'cm-extra-' + extraSideCount;
div.style.marginTop = '10px';
div.innerHTML = `
<div style="display:flex;align-items:center;gap:6px">
<div style="font-size:.72rem;font-weight:700;color:${color};text-transform:uppercase;letter-spacing:.07em;white-space:nowrap">Taraf ${name}</div>
<button type="button" onclick="removeExtraSide(${extraSideCount})"
style="margin-left:auto;background:none;border:none;color:rgba(255,80,80,.5);cursor:pointer;font-size:.8rem;padding:0 4px"></button>
</div>
<input class="mf-input" id="cm-extra-val-${extraSideCount}" maxlength="100" placeholder="Seçenek metni..." style="margin-top:4px">`;
wrap.appendChild(div);
if (extraSideCount >= 4) document.getElementById('cm-add-side-btn').style.display = 'none';
}
function removeExtraSide(n) {
document.getElementById('cm-extra-' + n)?.remove();
extraSideCount = Math.max(0, extraSideCount - 1);
document.getElementById('cm-add-side-btn').style.display = '';
// Renumber visible extras
const extras = document.getElementById('cm-extra-sides').children;
extraSideCount = extras.length;
}
async function submitTribunal() {
const err = document.getElementById('cm-error');
err.style.display = 'none';
const animeId = document.getElementById('cm-anime-id').value;
const question = document.getElementById('cm-question').value.trim();
const sideA = document.getElementById('cm-side-a').value.trim();
const sideB = document.getElementById('cm-side-b').value.trim();
const closesAt = document.getElementById('cm-closes-at').value;
// Extra sides
const extraSides = [];
for (let i = 1; i <= 4; i++) {
const v = document.getElementById('cm-extra-val-' + i)?.value.trim();
if (v) extraSides.push(v);
}
if (!animeId) { err.textContent = 'Anime seçin.'; err.style.display = 'block'; return; }
if (question.length < 10) { err.textContent = 'Soru en az 10 karakter olmalı.'; err.style.display = 'block'; return; }
if (!sideA || !sideB) { err.textContent = 'A ve B taraflarını doldurun.'; err.style.display = 'block'; return; }
const r = await fetch('/mahkeme', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json' },
body: JSON.stringify({ anime_id: animeId, question, side_a: sideA, side_b: sideB, extra_sides: extraSides, closes_at: closesAt || null }),
});
const d = await r.json();
if (d.url) window.location.href = d.url;
else { err.textContent = d.error || 'Hata oluştu.'; err.style.display = 'block'; }
}
</script>
@endpush
@@ -0,0 +1,253 @@
@extends('frontend.layouts.app')
@section('title', 'Mahkeme: ' . Str::limit($tribunal->question, 60) . ' | Animexe')
@php
// Sabit renk paleti — a,b,c,d,e,f
$palette = [
'a' => ['bg'=>'rgba(0,200,100,.08)', 'bg2'=>'rgba(0,200,100,.22)', 'border'=>'rgba(0,200,100,.25)', 'border2'=>'#00c864', 'text'=>'#00c864', 'bar'=>'#00c864'],
'b' => ['bg'=>'rgba(255,60,60,.08)', 'bg2'=>'rgba(255,60,60,.22)', 'border'=>'rgba(255,60,60,.25)', 'border2'=>'#ff3c3c', 'text'=>'#ff3c3c', 'bar'=>'#ff3c3c'],
'c' => ['bg'=>'rgba(59,130,246,.08)', 'bg2'=>'rgba(59,130,246,.22)', 'border'=>'rgba(59,130,246,.25)', 'border2'=>'#3b82f6', 'text'=>'#3b82f6', 'bar'=>'#3b82f6'],
'd' => ['bg'=>'rgba(245,158,11,.08)', 'bg2'=>'rgba(245,158,11,.22)', 'border'=>'rgba(245,158,11,.25)', 'border2'=>'#f59e0b', 'text'=>'#f59e0b', 'bar'=>'#f59e0b'],
'e' => ['bg'=>'rgba(167,139,250,.08)','bg2'=>'rgba(167,139,250,.22)','border'=>'rgba(167,139,250,.25)','border2'=>'#a78bfa','text'=>'#a78bfa','bar'=>'#a78bfa'],
'f' => ['bg'=>'rgba(236,72,153,.08)', 'bg2'=>'rgba(236,72,153,.22)', 'border'=>'rgba(236,72,153,.25)', 'border2'=>'#ec4899','text'=>'#ec4899','bar'=>'#ec4899'],
];
$cols = count($allSides) <= 2 ? '1fr 1fr' : (count($allSides) <= 3 ? '1fr 1fr 1fr' : 'repeat(auto-fit,minmax(140px,1fr))');
@endphp
@push('styles')
<style>
.tb-wrap{max-width:900px;margin:0 auto;padding:calc(var(--nav-h) + 32px) 20px 60px}
.tb-anime-link{font-size:.75rem;color:rgba(255,45,125,.8);font-weight:700;text-transform:uppercase;letter-spacing:.08em;text-decoration:none;display:inline-block;margin-bottom:12px}
.tb-question{font-size:1.5rem;font-weight:900;line-height:1.35;margin-bottom:14px}
.tb-status{display:inline-block;padding:3px 12px;border-radius:20px;font-size:.72rem;font-weight:700;margin-bottom:20px}
.tb-status.open{background:rgba(0,200,100,.15);color:#00c864;border:1px solid rgba(0,200,100,.25)}
.tb-status.closed{background:rgba(255,255,255,.06);color:rgba(255,255,255,.4);border:1px solid rgba(255,255,255,.1)}
/* Vote Card */
.tb-votebar{background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:16px;padding:24px;margin-bottom:24px}
.tb-sides{display:grid;gap:10px;margin-bottom:20px;grid-template-columns:{{ $cols }}}
.tb-vote-btn{padding:13px 10px;border-radius:12px;border:2px solid transparent;cursor:pointer;font-weight:800;font-size:.88rem;font-family:inherit;transition:all .2s;text-align:center;line-height:1.3}
/* Multi-bar */
.tb-multibar{display:flex;flex-direction:column;gap:8px}
.tb-bar-row{display:flex;align-items:center;gap:10px}
.tb-bar-label{font-size:.72rem;font-weight:700;white-space:nowrap;min-width:80px;max-width:120px;overflow:hidden;text-overflow:ellipsis}
.tb-bar-track{flex:1;height:8px;border-radius:99px;background:rgba(255,255,255,.07);overflow:hidden}
.tb-bar-fill{height:100%;border-radius:99px;transition:width .5s cubic-bezier(.22,1,.36,1)}
.tb-bar-pct{font-size:.72rem;color:rgba(255,255,255,.4);min-width:40px;text-align:right}
/* Arguments */
.tb-args{margin-top:28px}
.tb-args-title{font-size:.8rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,.3);margin-bottom:16px}
.tb-arg-card{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);border-radius:12px;padding:16px;margin-bottom:10px}
.tb-arg-side-badge{display:inline-block;padding:2px 10px;border-radius:20px;font-size:.68rem;font-weight:800;margin-bottom:8px}
.tb-arg-body{font-size:.9rem;line-height:1.5;margin-bottom:10px}
.tb-arg-meta{display:flex;align-items:center;gap:12px;font-size:.72rem;color:rgba(255,255,255,.3)}
.tb-arg-vote-btn{background:none;border:1px solid rgba(255,255,255,.1);border-radius:20px;padding:3px 12px;color:rgba(255,255,255,.4);cursor:pointer;font-size:.72rem;font-family:inherit;display:inline-flex;align-items:center;gap:5px;transition:all .15s}
.tb-arg-vote-btn:hover,.tb-arg-vote-btn.on{background:rgba(255,215,0,.12);border-color:rgba(255,215,0,.3);color:#ffd700}
/* Argue form */
.tb-argue-form{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);border-radius:14px;padding:20px;margin-top:24px}
.tb-argue-title{font-size:.88rem;font-weight:700;margin-bottom:12px}
.tb-argue-sides{display:grid;gap:8px;margin-bottom:12px;grid-template-columns:{{ $cols }}}
.tb-argue-side-btn{padding:9px 8px;border-radius:10px;cursor:pointer;font-weight:700;font-size:.8rem;font-family:inherit;border:2px solid transparent;transition:all .15s;text-align:center}
textarea.tb-inp{width:100%;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:10px 14px;color:#fff;font-size:.88rem;font-family:inherit;resize:none;outline:none;margin-bottom:10px;box-sizing:border-box}
.tb-submit-btn{padding:10px 24px;border-radius:10px;border:none;background:rgba(255,45,125,.2);color:#ff2d7d;font-weight:700;font-size:.88rem;cursor:pointer;font-family:inherit;transition:background .15s}
.tb-submit-btn:hover{background:rgba(255,45,125,.35)}
</style>
@endpush
@section('content')
<div class="tb-wrap">
<a class="tb-anime-link" href="{{ $tribunal->anime?->detailUrl ?? '#' }}">
<i class="bi bi-arrow-left"></i> {{ $tribunal->anime->title }}
</a>
<div class="tb-status {{ $tribunal->status }}">
{{ $tribunal->status === 'open' ? '🟢 Dava Açık' : '🔴 Dava Kapalı' }}
@if($tribunal->closes_at && $tribunal->status === 'open')
· {{ $tribunal->closes_at->diffForHumans() }} kapanıyor
@endif
</div>
<div class="tb-question">{{ $tribunal->question }}</div>
{{-- Oy sistemi --}}
<div class="tb-votebar">
<div class="tb-sides">
@foreach($allSides as $key => $label)
@php $c = $palette[$key] ?? $palette['a']; @endphp
<button id="vote-{{ $key }}" class="tb-vote-btn"
style="background:{{ $c['bg'] }};color:{{ $c['text'] }};border-color:{{ $c['border'] }}"
data-bg="{{ $c['bg'] }}" data-bg2="{{ $c['bg2'] }}" data-border="{{ $c['border'] }}" data-border2="{{ $c['border2'] }}"
onclick="castVote('{{ $key }}')">
{{ $label }}
<div style="font-size:.7rem;font-weight:400;opacity:.7;margin-top:3px">
<span id="vcnt-{{ $key }}">{{ $voteCounts[$key] ?? 0 }}</span> oy
· <span id="vpct-{{ $key }}">{{ $total > 0 ? round(($voteCounts[$key]??0)/$total*100) : 0 }}%</span>
</div>
</button>
@endforeach
</div>
{{-- Çok taraflı bar --}}
<div class="tb-multibar">
@foreach($allSides as $key => $label)
@php $c = $palette[$key] ?? $palette['a']; $pct = $total > 0 ? round(($voteCounts[$key]??0)/$total*100) : 0; @endphp
<div class="tb-bar-row">
<div class="tb-bar-label" style="color:{{ $c['text'] }}">{{ $label }}</div>
<div class="tb-bar-track">
<div class="tb-bar-fill" id="bar-{{ $key }}" style="width:{{ $pct }}%;background:{{ $c['bar'] }}"></div>
</div>
<div class="tb-bar-pct" id="bpct-{{ $key }}">{{ $pct }}%</div>
</div>
@endforeach
</div>
<div style="margin-top:10px;font-size:.72rem;color:rgba(255,255,255,.25)">
Toplam <span id="total-votes">{{ $total }}</span> oy
@auth @if($myVote) · <span style="color:rgba(255,255,255,.5)">Oyunuz: <strong>{{ $allSides[$myVote] ?? $myVote }}</strong></span> @endif @endauth
</div>
</div>
{{-- Argümanlar --}}
<div class="tb-args">
<div class="tb-args-title"><i class="bi bi-chat-quote"></i> Argümanlar</div>
<div id="arg-list">
@forelse($arguments as $arg)
@php $c2 = $palette[$arg['side']] ?? $palette['a']; @endphp
<div class="tb-arg-card" id="arg-{{ $arg['id'] }}">
<span class="tb-arg-side-badge"
style="background:{{ $c2['bg'] }};color:{{ $c2['text'] }}">{{ $allSides[$arg['side']] ?? $arg['side'] }}</span>
<div class="tb-arg-body">{{ $arg['body'] }}</div>
<div class="tb-arg-meta">
<span>{{ $arg['name'] ?? $arg['username'] }}</span>
<span>{{ $arg['created_at'] }}</span>
<button class="tb-arg-vote-btn {{ $arg['voted'] ? 'on' : '' }}" id="avgbtn-{{ $arg['id'] }}"
onclick="argVote({{ $arg['id'] }})">
<i class="bi bi-hand-thumbs-up"></i>
<span id="avgcnt-{{ $arg['id'] }}">{{ $arg['vote_count'] }}</span>
</button>
</div>
</div>
@empty
<div id="no-args" style="text-align:center;padding:30px;color:rgba(255,255,255,.25);font-size:.85rem">
Henüz argüman girilmemiş. İlk sen gir!
</div>
@endforelse
</div>
</div>
{{-- Argüman formu --}}
@auth
@if(!$myArgument && $tribunal->status === 'open')
<div class="tb-argue-form">
<div class="tb-argue-title">⚔️ Senin Argümanın Hangi taraftasın?</div>
<div class="tb-argue-sides">
@foreach($allSides as $key => $label)
@php $c3 = $palette[$key] ?? $palette['a']; @endphp
<button id="arg-side-{{ $key }}" class="tb-argue-side-btn"
style="background:{{ $c3['bg'] }};color:{{ $c3['text'] }};border-color:{{ $c3['border'] }}"
data-bg="{{ $c3['bg'] }}" data-bg2="{{ $c3['bg2'] }}" data-border="{{ $c3['border'] }}" data-border2="{{ $c3['border2'] }}"
onclick="selectArgSide('{{ $key }}')">{{ $label }}</button>
@endforeach
</div>
<textarea class="tb-inp" id="arg-body" rows="3" maxlength="500" placeholder="Argümanını yaz (min 10, max 500 karakter)..."></textarea>
<div id="arg-err" style="color:#ff4444;font-size:.78rem;margin-bottom:8px;display:none"></div>
<button class="tb-submit-btn" onclick="submitArg()">Argümanı Gönder</button>
</div>
@elseif($myArgument)
<div style="padding:14px;background:rgba(0,200,100,.06);border:1px solid rgba(0,200,100,.2);border-radius:12px;font-size:.85rem;color:rgba(255,255,255,.5);margin-top:24px">
Argümanınızı gönderdiniz.
</div>
@endif
@else
<div style="margin-top:20px;text-align:center;font-size:.85rem;color:rgba(255,255,255,.3)">
<a href="{{ route('frontend.login') }}" style="color:#ff2d7d">Giriş yap</a> argüman girmek için
</div>
@endauth
</div>
@endsection
@push('scripts')
<script>
const CSRF = document.querySelector('meta[name="csrf-token"]')?.content ?? '';
const IS_AUTH = {{ auth()->check() ? 'true' : 'false' }};
const MY_VOTE = {{ $myVote ? json_encode($myVote) : 'null' }};
let selectedArgSide = null;
// İlk yüklemede aktif oyu vurgula
if (MY_VOTE) activateVoteBtn(MY_VOTE);
function activateVoteBtn(side) {
document.querySelectorAll('.tb-vote-btn').forEach(b => {
const isMe = b.id === 'vote-' + side;
b.style.background = isMe ? b.dataset.bg2 : b.dataset.bg;
b.style.borderColor = isMe ? b.dataset.border2 : b.dataset.border;
});
}
function selectArgSide(s) {
selectedArgSide = s;
document.querySelectorAll('.tb-argue-side-btn').forEach(b => {
const isMe = b.id === 'arg-side-' + s;
b.style.background = isMe ? b.dataset.bg2 : b.dataset.bg;
b.style.borderColor = isMe ? b.dataset.border2 : b.dataset.border;
});
}
async function castVote(side) {
if (!IS_AUTH) { window.location.href = '{{ route('frontend.login') }}'; return; }
const r = await fetch('{{ route('tribunal.vote', $tribunal) }}', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json' },
body: JSON.stringify({ side }),
});
const d = await r.json();
updateVoteBar(d.voted, d.counts, d.total);
}
function updateVoteBar(voted, counts, total) {
activateVoteBtn(voted);
document.getElementById('total-votes').textContent = total;
for (const [key, cnt] of Object.entries(counts)) {
const pct = total > 0 ? Math.round(cnt / total * 100) : 0;
const fill = document.getElementById('bar-' + key);
const pctEl = document.getElementById('bpct-' + key);
const vcnt = document.getElementById('vcnt-' + key);
const vpct = document.getElementById('vpct-' + key);
if (fill) fill.style.width = pct + '%';
if (pctEl) pctEl.textContent = pct + '%';
if (vcnt) vcnt.textContent = cnt;
if (vpct) vpct.textContent = pct + '%';
}
}
async function argVote(id) {
if (!IS_AUTH) { window.location.href = '{{ route('frontend.login') }}'; return; }
const r = await fetch(`/arguman/${id}/oy`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json' },
body: JSON.stringify({}),
});
const d = await r.json();
document.getElementById('avgcnt-' + id).textContent = d.vote_count;
document.getElementById('avgbtn-' + id).classList.toggle('on', d.voted);
}
async function submitArg() {
const err = document.getElementById('arg-err');
const body = document.getElementById('arg-body').value.trim();
err.style.display = 'none';
if (!selectedArgSide) { err.textContent = 'Taraf seçin.'; err.style.display = 'block'; return; }
if (body.length < 10) { err.textContent = 'Argüman en az 10 karakter olmalı.'; err.style.display = 'block'; return; }
const r = await fetch('{{ route('tribunal.argue', $tribunal) }}', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json' },
body: JSON.stringify({ side: selectedArgSide, body }),
});
const d = await r.json();
if (d.ok) window.location.reload();
else { err.textContent = d.error || 'Hata.'; err.style.display = 'block'; }
}
</script>
@endpush
@@ -0,0 +1,274 @@
@extends('frontend.layouts.app')
@section('title', 'Watch Party — ' . $party->episode->anime->title . ' | Animexe')
@push('styles')
<style>
body{background:#05050e}
#pg{padding-top:0}
.wp-wrap{display:flex;max-width:1600px;margin:0 auto;padding-top:var(--nav-h);min-height:calc(100vh - var(--nav-h));gap:0}
.wp-main{flex:1;min-width:0;background:#020208;display:flex;flex-direction:column}
.wp-side{width:320px;flex-shrink:0;background:rgba(10,10,22,.98);border-left:1px solid rgba(255,45,125,.15);display:flex;flex-direction:column;height:calc(100vh - var(--nav-h));position:sticky;top:var(--nav-h)}
/* Video */
.wp-vwrap{position:relative;width:100%;aspect-ratio:16/9;background:#000}
.wp-vwrap iframe,.wp-vwrap video{position:absolute;inset:0;width:100%;height:100%;border:none}
/* Host bar */
.wp-hostbar{padding:12px 16px;background:rgba(255,45,125,.06);border-bottom:1px solid rgba(255,45,125,.15);display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.wp-roomcode{font-size:1.4rem;font-weight:900;letter-spacing:.18em;color:#ff2d7d;cursor:pointer;user-select:all}
.wp-hostbtn{padding:6px 16px;border-radius:20px;border:1px solid rgba(255,45,125,.4);background:rgba(255,45,125,.1);color:#ff2d7d;font-size:.8rem;font-weight:700;cursor:pointer;font-family:inherit;transition:all .15s}
.wp-hostbtn:hover{background:rgba(255,45,125,.25)}
.wp-sync-status{font-size:.72rem;color:rgba(255,255,255,.3);margin-left:auto}
/* Members */
.wp-members{padding:12px 16px;border-bottom:1px solid rgba(255,255,255,.06)}
.wp-members-title{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,.3);margin-bottom:8px}
.wp-member-list{display:flex;flex-wrap:wrap;gap:6px}
.wp-member{display:flex;align-items:center;gap:6px;padding:4px 10px;background:rgba(255,255,255,.06);border-radius:20px;font-size:.76rem;color:#fff}
.wp-member.host{border:1px solid rgba(255,215,0,.3);background:rgba(255,215,0,.06)}
/* Chat */
.wp-chat{flex:1;overflow-y:auto;padding:12px 16px;display:flex;flex-direction:column;gap:8px;scroll-behavior:smooth}
.wc-msg{max-width:100%}
.wc-meta{font-size:.68rem;color:rgba(255,255,255,.3);margin-bottom:3px}
.wc-bubble{display:inline-block;padding:7px 12px;border-radius:0 10px 10px 10px;background:rgba(255,255,255,.07);font-size:.84rem;color:#fff;word-break:break-word;max-width:100%;line-height:1.4}
.wc-mine .wc-bubble{background:rgba(255,45,125,.15);border-radius:10px 0 10px 10px}
.wc-mine{align-self:flex-end;text-align:right}
.wc-mine .wc-meta{text-align:right}
/* Chat input */
.wp-chat-input{padding:12px 16px;border-top:1px solid rgba(255,255,255,.07);display:flex;gap:8px;flex-shrink:0}
.wp-chat-input input{flex:1;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:9px 14px;color:#fff;font-size:.84rem;font-family:inherit;outline:none}
.wp-chat-input button{padding:9px 16px;border-radius:10px;border:none;background:rgba(255,45,125,.2);color:#ff2d7d;font-size:.9rem;cursor:pointer;flex-shrink:0;transition:background .15s}
.wp-chat-input button:hover{background:rgba(255,45,125,.35)}
/* Episode info */
.wp-ep-info{padding:14px 16px;border-bottom:1px solid rgba(255,255,255,.06);display:flex;gap:12px;align-items:center}
.wp-ep-info img{width:48px;height:72px;border-radius:8px;object-fit:cover;flex-shrink:0}
.wp-ep-title{font-size:.84rem;font-weight:700;color:#fff;margin-bottom:2px}
.wp-ep-sub{font-size:.72rem;color:rgba(255,255,255,.35)}
</style>
@endpush
@section('content')
<div class="wp-wrap">
{{-- ── Ana video alanı ─────────────────────────────────────────── --}}
<div class="wp-main">
{{-- Video --}}
<div class="wp-vwrap" id="wp-vwrap">
@if($party->episode->m3u8_url || $party->episode->video_url)
<video id="wp-vd" controls playsinline preload="metadata"
@if($party->episode->m3u8_url) data-src="{{ $party->episode->m3u8_url }}" @endif
@if($party->episode->video_url) src="{{ $party->episode->video_url }}" @endif>
</video>
@else
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:rgba(255,255,255,.3);font-size:.9rem">
<div style="text-align:center"><i class="bi bi-play-circle" style="font-size:3rem;display:block;margin-bottom:8px"></i>Video yüklenemedi</div>
</div>
@endif
</div>
{{-- Host kontrol barı --}}
<div class="wp-hostbar">
<div>
<div style="font-size:.68rem;color:rgba(255,255,255,.3);margin-bottom:2px">Oda Kodu</div>
<div class="wp-roomcode" onclick="copyCode()" title="Kopyalamak için tıkla">{{ $party->room_code }}</div>
</div>
<div>
<div style="font-size:.68rem;color:rgba(255,255,255,.3);margin-bottom:4px">
Ev Sahibi: <span id="wp-host-name" style="color:#fff">{{ $party->host->name ?? '—' }}</span>
@auth @if(auth()->id() === $party->host_user_id)
<span style="background:rgba(255,215,0,.15);color:#ffd700;padding:1px 7px;border-radius:10px;font-size:.65rem;margin-left:5px">Sensin</span>
@endif @endauth
</div>
<div id="wp-member-count" style="font-size:.72rem;color:rgba(255,255,255,.35)">0 üye aktif</div>
</div>
@auth @if(auth()->id() === $party->host_user_id)
<button class="wp-hostbtn" onclick="hostPlay()"><i class="bi bi-play-fill"></i> Oynat</button>
<button class="wp-hostbtn" onclick="hostPause()"><i class="bi bi-pause-fill"></i> Durdur</button>
@endif @endauth
<span class="wp-sync-status" id="wp-sync-status"> bekleniyor...</span>
<a href="{{ route('watch', [$party->episode->anime->slug, $party->episode->season->season_number ?? 1, $party->episode->episode_number]) }}"
style="margin-left:auto;font-size:.76rem;color:rgba(255,255,255,.3);text-decoration:none">
<i class="bi bi-box-arrow-up-right"></i> Tam oynatıcı
</a>
</div>
</div>
{{-- ── Sağ panel: Üyeler + Chat ─────────────────────────────────── --}}
<div class="wp-side">
{{-- Bölüm bilgisi --}}
<div class="wp-ep-info">
@if($party->episode->anime->coverUrl)
<img src="{{ $party->episode->anime->coverUrl }}" alt="">
@endif
<div>
<div class="wp-ep-title">{{ $party->episode->anime->title }}</div>
<div class="wp-ep-sub">S{{ $party->episode->season->season_number ?? 1 }}E{{ $party->episode->episode_number }}
{{ $party->episode->title ? '— ' . $party->episode->title : '' }}</div>
</div>
</div>
{{-- Aktif üyeler --}}
<div class="wp-members">
<div class="wp-members-title"><i class="bi bi-people"></i> Aktif Üyeler</div>
<div class="wp-member-list" id="wp-members"></div>
</div>
{{-- Chat --}}
<div class="wp-chat" id="wp-chat"></div>
{{-- Chat input --}}
@auth
<div class="wp-chat-input">
<input type="text" id="wp-chat-inp" placeholder="Mesaj yaz..." maxlength="200"
onkeydown="if(event.key==='Enter')sendChatMsg()">
<button onclick="sendChatMsg()"><i class="bi bi-send-fill"></i></button>
</div>
@else
<div style="padding:12px;text-align:center;font-size:.78rem;color:rgba(255,255,255,.3)">
<a href="{{ route('frontend.login') }}" style="color:#ff2d7d">Giriş yap</a> sohbete katıl
</div>
@endauth
</div>
</div>
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
const WP_CSRF = document.querySelector('meta[name="csrf-token"]')?.content ?? '';
const WP_ROOM = '{{ $party->room_code }}';
const WP_IS_HOST = {{ auth()->check() && auth()->id() === $party->host_user_id ? 'true' : 'false' }};
const WP_ME_NAME = {{ auth()->check() ? json_encode(auth()->user()->name) : 'null' }};
const WP_IS_AUTH = {{ auth()->check() ? 'true' : 'false' }};
// HLS init
const vd = document.getElementById('wp-vd');
if (vd) {
const src = vd.dataset.src;
if (src && Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(src);
hls.attachMedia(vd);
} else if (src && vd.canPlayType('application/vnd.apple.mpegurl')) {
vd.src = src;
}
}
// ── Chat (basit localStorage tabanlı, polling) ────────────────
// Gerçek WebSocket olmadığı için sync sırasında chat mesajlarını da taşı
let chatMessages = [];
let lastChatTs = 0;
function addChatMsg(msg, isMine) {
chatMessages.push({ ...msg, mine: isMine });
renderChat();
}
function renderChat() {
const el = document.getElementById('wp-chat');
if (!el) return;
el.innerHTML = chatMessages.map(m => `
<div class="wc-msg ${m.mine ? 'wc-mine' : ''}">
<div class="wc-meta">${escHtml(m.name || '?')} · ${m.time || ''}</div>
<div class="wc-bubble">${escHtml(m.body)}</div>
</div>`).join('');
el.scrollTop = el.scrollHeight;
}
function escHtml(s) {
return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function sendChatMsg() {
const inp = document.getElementById('wp-chat-inp');
const body = inp?.value.trim();
if (!body) return;
inp.value = '';
const now = new Date().toLocaleTimeString('tr-TR', { hour:'2-digit', minute:'2-digit' });
addChatMsg({ name: WP_ME_NAME, body, time: now }, true);
// Broadcast via sync payload (next poll)
pendingChatMsg = { name: WP_ME_NAME, body, time: now };
}
let pendingChatMsg = null;
// ── Party Sync ─────────────────────────────────────────────────
function renderMembers(members) {
const el = document.getElementById('wp-members');
const cnt = document.getElementById('wp-member-count');
if (el) el.innerHTML = members.map(m => `
<div class="wp-member ${m.is_host ? 'host' : ''}">
${m.is_host ? '<i class="bi bi-star-fill" style="font-size:.6rem;color:#ffd700"></i>' : ''}
${escHtml(m.name || '?')}
</div>`).join('');
if (cnt) cnt.textContent = members.length + ' üye aktif';
}
async function doSync() {
const body = WP_IS_HOST
? { current_sec: Math.floor(vd?.currentTime ?? 0), is_playing: !(vd?.paused ?? true) }
: {};
try {
const r = await fetch(`/party/${WP_ROOM}/sync`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': WP_CSRF, 'Accept': 'application/json' },
body: JSON.stringify(body),
});
const d = await r.json();
renderMembers(d.members || []);
// Non-host senkron
if (!WP_IS_HOST && vd) {
const diff = Math.abs(vd.currentTime - d.current_sec);
if (diff > 2) vd.currentTime = d.current_sec;
if (d.is_playing && vd.paused) vd.play().catch(()=>{});
if (!d.is_playing && !vd.paused) vd.pause();
}
document.getElementById('wp-sync-status').textContent = '✅ senkron ' + new Date().toLocaleTimeString('tr-TR',{hour:'2-digit',minute:'2-digit',second:'2-digit'});
pendingChatMsg = null;
} catch(e) {
document.getElementById('wp-sync-status').textContent = '❌ bağlantı kesildi';
}
}
function hostPlay() { if (vd) { vd.play().catch(()=>{}); } }
function hostPause() { if (vd) vd.pause(); }
function copyCode() {
navigator.clipboard?.writeText(WP_ROOM).then(() => {
if (window.showToast) showToast('Oda kodu kopyalandı: ' + WP_ROOM, 'success');
else alert('Oda kodu kopyalandı: ' + WP_ROOM);
});
}
// İlk join
if (WP_IS_AUTH) {
fetch(`/party/${WP_ROOM}/join`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': WP_CSRF, 'Accept': 'application/json' },
body: JSON.stringify({}),
}).then(r => r.json()).then(d => {
if (d.ok) {
renderMembers(d.members || []);
if (!WP_IS_HOST && vd && d.current_sec > 0) {
vd.currentTime = d.current_sec;
}
}
}).catch(()=>{});
}
setInterval(doSync, 3000);
// Ayrılma temizliği
window.addEventListener('beforeunload', () => {
navigator.sendBeacon(`/party/${WP_ROOM}/leave`, new Blob([JSON.stringify({})], { type: 'application/json' }));
});
</script>
@endpush