Initial commit: Animexe Laravel platform
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user