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
@@ -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