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