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,133 @@
@extends('admin.layouts.app')
@section('title', 'İzin Düzenle — ' . $moderator->name)
@section('page-title', $moderator->name . ' — İzin Yönetimi')
@push('styles')
<style>
.perm-switch-row {
display:flex; align-items:center; justify-content:space-between;
padding:11px 0; border-bottom:1px solid var(--border);
}
.perm-switch-row:last-child { border-bottom:none; }
</style>
@endpush
@section('content')
<div class="d-flex align-items-center gap-3 mb-4">
<a href="{{ route('admin.moderators.index') }}" class="btn btn-secondary btn-sm">
<i class="bi bi-arrow-left"></i> Geri Dön
</a>
<div>
<div style="font-size:13px;color:var(--text2)">{{ $moderator->email }}</div>
</div>
</div>
<form method="POST" action="{{ route('admin.moderators.permissions', $moderator) }}">
@csrf
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
<div style="display:flex;align-items:center;gap:10px">
<div style="width:40px;height:40px;border-radius:12px;background:linear-gradient(135deg,#f59e0b,#d97706);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:15px;color:#000">
{{ strtoupper(substr($moderator->name,0,1)) }}
</div>
<div>
<div style="font-size:15px;font-weight:700;color:var(--text)">{{ $moderator->name }}</div>
<div style="font-size:12px;color:var(--text2)">
<span style="color:var(--warning);font-weight:700" id="permCount">{{ count($permissions) }}</span> aktif izin
</div>
</div>
</div>
<div class="d-flex gap-2">
<button type="button" class="btn btn-sm btn-secondary" onclick="selectAll(true)">
<i class="bi bi-check-all"></i> Tümünü Ver
</button>
<button type="button" class="btn btn-sm btn-outline-danger" onclick="selectAll(false)">
<i class="bi bi-x-circle"></i> Tümünü Kaldır
</button>
<button type="submit" class="btn btn-warning btn-sm">
<i class="bi bi-floppy"></i> Kaydet
</button>
</div>
</div>
<div class="row g-3">
@foreach($groups as $groupName => $keys)
@php $groupGranted = collect(array_keys($keys))->filter(fn($k) => isset($permissions[$k]))->count(); @endphp
<div class="col-md-6">
<div class="card h-100">
<div class="card-header">
<i class="bi bi-folder-fill" style="color:var(--warning)"></i>
<h6>{{ $groupName }}</h6>
<span class="badge {{ $groupGranted>0?'bg-success':'bg-secondary' }} ms-auto">
{{ $groupGranted }}/{{ count($keys) }}
</span>
<button type="button" class="btn btn-sm btn-secondary" style="padding:3px 9px;font-size:11px"
onclick="toggleGroup(this, {{ collect(array_keys($keys))->toJson() }})">
Tümünü Seç
</button>
</div>
<div class="card-body">
@foreach($keys as $key => $label)
<div class="perm-switch-row" data-perm="{{ $key }}">
<div>
<div style="font-size:13px;font-weight:600;color:var(--text)">{{ $label }}</div>
<code style="font-size:11px;color:var(--text3)">{{ $key }}</code>
</div>
<div class="form-check form-switch mb-0">
<input class="form-check-input perm-toggle" type="checkbox"
name="permissions[]" value="{{ $key }}"
{{ isset($permissions[$key]) ? 'checked' : '' }}
data-perm="{{ $key }}"
id="perm_{{ str_replace('.','_',$key) }}"
role="switch"
style="cursor:pointer">
</div>
</div>
@endforeach
</div>
</div>
</div>
@endforeach
</div>
<div class="mt-4 d-flex gap-2">
<button type="submit" class="btn btn-warning">
<i class="bi bi-floppy"></i> İzinleri Kaydet
</button>
<button type="button" class="btn btn-secondary" onclick="selectAll(true)">
<i class="bi bi-check-all"></i> Tüm İzinleri Ver
</button>
<button type="button" class="btn btn-outline-danger" onclick="selectAll(false)">
<i class="bi bi-x-circle"></i> Tüm İzinleri Kaldır
</button>
</div>
</form>
@endsection
@push('scripts')
<script>
function updateCount() {
const n = document.querySelectorAll('.perm-toggle:checked').length;
document.getElementById('permCount').textContent = n;
}
document.querySelectorAll('.perm-toggle').forEach(cb => cb.addEventListener('change', updateCount));
function selectAll(state) {
document.querySelectorAll('.perm-toggle').forEach(cb => cb.checked = state);
updateCount();
}
function toggleGroup(btn, keys) {
const anyUnchecked = keys.some(k => !document.querySelector(`[data-perm="${k}"].perm-toggle`)?.checked);
keys.forEach(k => {
const el = document.querySelector(`[data-perm="${k}"].perm-toggle`);
if (el) el.checked = anyUnchecked;
});
btn.textContent = anyUnchecked ? 'Tümünü Kaldır' : 'Tümünü Seç';
updateCount();
}
</script>
@endpush
@@ -0,0 +1,189 @@
@extends('admin.layouts.app')
@section('title', 'Moderatör Yönetimi')
@section('page-title', 'Moderatör Yönetimi')
@section('content')
<div class="page-header">
<div>
<h5 style="font-size:18px;font-weight:700;color:var(--text);margin:0">Moderatör Yönetimi</h5>
<div style="font-size:13px;color:var(--text2);margin-top:2px">
{{ $moderators->total() }} aktif moderatör · {{ count($groups) }} izin grubu
</div>
</div>
<button class="btn btn-warning btn-sm" data-bs-toggle="modal" data-bs-target="#promoteModal">
<i class="bi bi-person-plus"></i> Moderatör Ekle
</button>
</div>
{{-- Tablo --}}
<div class="table-wrap mb-4">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th style="padding-left:20px">Kullanıcı</th>
<th style="text-align:center">İzin Sayısı</th>
<th>İzin Grupları</th>
<th>Katılım Tarihi</th>
<th style="text-align:right;padding-right:20px">İşlem</th>
</tr>
</thead>
<tbody>
@forelse($moderators as $mod)
@php
$perms = $mod->moderatorPermissions->pluck('permission');
$grantedGroups = collect($groups)->filter(fn($keys) => collect(array_keys($keys))->intersect($perms)->isNotEmpty());
@endphp
<tr>
<td style="padding-left:20px">
<div class="d-flex align-items-center gap-3">
<div class="user-avatar" style="border-radius:12px;background:linear-gradient(135deg,#f59e0b,#d97706)">
{{ strtoupper(substr($mod->name,0,1)) }}
</div>
<div>
<div style="font-size:13.5px;font-weight:700;color:var(--text)">{{ $mod->name }}</div>
<div style="font-size:12px;color:var(--text2)">{{ $mod->email }}</div>
</div>
</div>
</td>
<td style="text-align:center">
<span class="badge {{ $mod->moderator_permissions_count > 0 ? 'bg-success' : 'bg-secondary' }}">
{{ $mod->moderator_permissions_count }} izin
</span>
</td>
<td>
<div class="d-flex flex-wrap gap-1">
@foreach($grantedGroups->keys() as $gName)
<span class="badge bg-warning" style="color:#000;font-size:10.5px">{{ $gName }}</span>
@endforeach
@if($grantedGroups->isEmpty())
<span style="font-size:12px;color:var(--text3)"> izin yok </span>
@endif
</div>
</td>
<td style="font-size:12.5px;color:var(--text2)">{{ $mod->created_at->format('d.m.Y') }}</td>
<td style="text-align:right;padding-right:20px">
<div class="d-flex gap-2 justify-content-end">
<a href="{{ route('admin.moderators.edit',$mod) }}" class="btn btn-sm btn-outline-warning">
<i class="bi bi-sliders"></i> İzinler
</a>
<form method="POST" action="{{ route('admin.moderators.demote',$mod) }}"
onsubmit="return confirm('{{ addslashes($mod->name) }} moderatörlükten çıkarılsın mı?')">
@csrf
<button type="submit" class="btn btn-sm btn-outline-danger">
<i class="bi bi-person-dash"></i>
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="5">
<div class="empty-state">
<i class="bi bi-shield-check"></i>
<h6>Henüz moderatör yok</h6>
<p>Kullanıcıları moderatör olarak atayın.</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
{{-- İzin Grupları --}}
<div class="card">
<div class="card-header">
<i class="bi bi-key" style="color:var(--warning)"></i>
<h6>Mevcut İzin Grupları</h6>
</div>
<div class="card-body">
<div class="row g-3">
@foreach($groups as $groupName => $keys)
<div class="col-md-4">
<div style="padding:14px;border:1px solid rgba(245,158,11,.2);border-radius:var(--radius-sm);background:rgba(245,158,11,.04)">
<div style="font-size:12px;font-weight:800;color:var(--warning);text-transform:uppercase;letter-spacing:.8px;margin-bottom:10px">
<i class="bi bi-folder-fill me-1"></i>{{ $groupName }}
</div>
@foreach($keys as $key => $label)
<div style="display:flex;align-items:center;gap:7px;padding:3px 0">
<code style="font-size:11px;color:var(--info);background:var(--info-dim);padding:1px 5px;border-radius:4px">{{ $key }}</code>
<span style="font-size:12px;color:var(--text2)">{{ $label }}</span>
</div>
@endforeach
</div>
</div>
@endforeach
</div>
</div>
</div>
{{-- Promote Modal --}}
<div class="modal fade" id="promoteModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title" style="color:var(--warning)">
<i class="bi bi-person-plus me-2"></i>Moderatör Ekle
</h6>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST" action="{{ route('admin.moderators.promote') }}">
@csrf
<div class="modal-body">
<div class="p-3 rounded mb-3" style="background:var(--warning-dim);border:1px solid rgba(245,158,11,.25);font-size:13px;color:var(--text)">
Normal bir kullanıcıyı moderatör olarak atayın. Sonrasında İzinler butonuyla yetkileri belirleyebilirsiniz.
</div>
<label class="form-label">Kullanıcı Ara</label>
<input type="text" name="user_id" id="userSearch" class="form-control"
placeholder="İsim veya e-posta ile ara..." autocomplete="off">
<div id="userSearchResults" class="mt-1" style="position:relative;z-index:100"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">İptal</button>
<button type="submit" class="btn btn-warning">
<i class="bi bi-shield-plus"></i> Moderatör Yap
</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
let searchTimer;
const inp = document.getElementById('userSearch');
const res = document.getElementById('userSearchResults');
if (inp) {
inp.addEventListener('input', function() {
clearTimeout(searchTimer);
if (this.value.length < 2) { res.innerHTML=''; return; }
searchTimer = setTimeout(() => {
fetch(`/admin/users?q=${encodeURIComponent(this.value)}&json=1`)
.then(r => r.json())
.then(data => {
if (!data.length) { res.innerHTML = '<div style="font-size:12.5px;color:var(--text2);padding:8px">Kullanıcı bulunamadı.</div>'; return; }
res.innerHTML = data.map(u => `
<div class="d-flex align-items-center gap-2 p-2 mb-1 rounded" style="background:var(--surface2);border:1px solid var(--border);cursor:pointer" onclick="selectUser(${u.id},'${u.name}${u.email}')">
<div style="width:28px;height:28px;border-radius:8px;background:var(--accent-dim);color:var(--accent-lt);display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;flex-shrink:0">${u.name.charAt(0).toUpperCase()}</div>
<div>
<div style="font-size:13px;font-weight:600;color:var(--text)">${u.name}</div>
<div style="font-size:11.5px;color:var(--text2)">${u.email}</div>
</div>
</div>`).join('');
});
}, 300);
});
}
function selectUser(id, label) {
inp.value = id;
res.innerHTML = `<div style="font-size:12.5px;color:var(--success);padding:6px 0;display:flex;align-items:center;gap:6px"><i class="bi bi-check-circle-fill"></i>${label} seçildi</div>`;
}
</script>
@endpush