Files
aresv2/views/admin/partials/settings.html
Beyhan Oğur 4362c3b83f first commit
2026-04-26 21:33:39 +03:00

538 lines
34 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div class="container-fluid" hx-trigger="heroListChanged from:body" hx-get="/admin/content/settings" hx-target="this"
hx-swap="outerHTML">
<h2 class="mb-4">Ayarlar & Banner Yönetimi</h2>
<ul class="nav nav-tabs mb-4" id="settingsTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="general-tab" data-bs-toggle="tab" data-bs-target="#general"
type="button" role="tab">Genel Ayarlar</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="security-tab" data-bs-toggle="tab" data-bs-target="#security" type="button" role="tab">Güvenlik</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="hero-tab" data-bs-toggle="tab" data-bs-target="#hero" type="button"
role="tab">Banner (Hero) Yönetimi</button>
</li>
</ul>
<div class="tab-content" id="settingsTabContent">
<!-- General Settings Tab -->
<div class="tab-pane fade show active" id="general" role="tabpanel">
<div class="card border-0 shadow-sm">
<div class="card-body">
<form action="/admin/settings" method="POST" enctype="multipart/form-data">
<div class="row">
<div class="col-md-6 mb-3">
<label for="title" class="form-label">Site Başlığı</label>
<input type="text" class="form-control" id="title" name="title"
value="{{ .Setting.Title }}">
</div>
<div class="col-md-6 mb-3">
<label for="meta_title" class="form-label">Meta Başlık</label>
<input type="text" class="form-control" id="meta_title" name="meta_title"
value="{{ .Setting.MetaTitle }}">
</div>
</div>
<div class="mb-3">
<label for="meta_description" class="form-label">Meta Açıklama</label>
<textarea class="form-control" id="meta_description" name="meta_description"
rows="2">{{ .Setting.MetaDescription }}</textarea>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="email" class="form-label">E-posta</label>
<input type="email" class="form-control" id="email" name="email"
value="{{ .Setting.Email }}">
</div>
<div class="col-md-6 mb-3">
<label for="phone" class="form-label">Telefon</label>
<input type="text" class="form-control" id="phone" name="phone"
value="{{ .Setting.Phone }}">
</div>
</div>
<div class="mb-3">
<label for="address" class="form-label">Adres</label>
<textarea class="form-control" id="address" name="address"
rows="2">{{ .Setting.Address }}</textarea>
</div>
<div class="mb-3">
<label for="url" class="form-label">Site URL</label>
<input type="text" class="form-control" id="url" name="url" value="{{ .Setting.URL }}">
</div>
<h5 class="mt-4">Logo Ayarları</h5>
<div class="row">
<!-- White Logo (WLogo) -->
<div class="col-md-6">
<div class="card border p-3 mb-3">
<h6 class="mb-3">Beyaz Logo (Genellikle Koyu Zemin İçin)</h6>
<div class="mb-3">
<label class="form-label">Logo Yükle</label>
<input type="file" class="form-control" name="w_logo" accept="image/*">
{{ if .Setting.WLogo }}
<div class="mt-2 text-center p-2 logo-preview logo-preview-light border rounded">
<img src="{{ .Setting.WLogo }}" alt="WLogo" style="max-height: 50px;">
</div>
{{ end }}
</div>
<div class="row g-2">
<div class="col-6">
<label class="form-label small text-muted mb-0">Genişlik</label>
<input type="number" class="form-control form-control-sm" name="w_width"
placeholder="Otomatik" value="{{ .Setting.WWidth }}">
</div>
<div class="col-6">
<label class="form-label small text-muted mb-0">Yükseklik</label>
<input type="number" class="form-control form-control-sm" name="w_height"
placeholder="Otomatik" value="{{ .Setting.WHeight }}">
</div>
<div class="col-6">
<label class="form-label small text-muted mb-0">Kalite (1-100)</label>
<input type="number" class="form-control form-control-sm" name="w_quality"
placeholder="80" value="{{ .Setting.WQuality }}">
</div>
<div class="col-6">
<label class="form-label small text-muted mb-0">Format</label>
<select class="form-select form-select-sm" name="w_format">
<option value="avif" {{ if eq .Setting.WFormat "avif" }}selected{{ end
}}>AVIF (Önerilen)</option>
<option value="webp" {{ if eq .Setting.WFormat "webp" }}selected{{ end
}}>WebP</option>
<option value="png" {{ if eq .Setting.WFormat "png" }}selected{{ end }}>
PNG</option>
<option value="jpg" {{ if eq .Setting.WFormat "jpg" }}selected{{ end }}>
JPG</option>
</select>
</div>
</div>
</div>
</div>
<!-- Black Logo (BLogo) -->
<div class="col-md-6">
<div class="card border p-3 mb-3">
<h6 class="mb-3">Siyah Logo (Genellikle Beyaz Zemin İçin)</h6>
<div class="mb-3">
<label class="form-label">Logo Yükle</label>
<input type="file" class="form-control" name="b_logo" accept="image/*">
{{ if .Setting.BLogo }}
<div class="mt-2 text-center p-2 logo-preview logo-preview-light border rounded">
<img src="{{ .Setting.BLogo }}" alt="BLogo" style="max-height: 50px;">
</div>
{{ end }}
</div>
<div class="row g-2">
<div class="col-6">
<label class="form-label small text-muted mb-0">Genişlik</label>
<input type="number" class="form-control form-control-sm" name="b_width"
placeholder="Otomatik" value="{{ .Setting.BWidth }}">
</div>
<div class="col-6">
<label class="form-label small text-muted mb-0">Yükseklik</label>
<input type="number" class="form-control form-control-sm" name="b_height"
placeholder="Otomatik" value="{{ .Setting.BHeight }}">
</div>
<div class="col-6">
<label class="form-label small text-muted mb-0">Kalite (1-100)</label>
<input type="number" class="form-control form-control-sm" name="b_quality"
placeholder="80" value="{{ .Setting.BQuality }}">
</div>
<div class="col-6">
<label class="form-label small text-muted mb-0">Format</label>
<select class="form-select form-select-sm" name="b_format">
<option value="avif" {{ if eq .Setting.BFormat "avif" }}selected{{ end
}}>AVIF (Önerilen)</option>
<option value="webp" {{ if eq .Setting.BFormat "webp" }}selected{{ end
}}>WebP</option>
<option value="png" {{ if eq .Setting.BFormat "png" }}selected{{ end }}>
PNG</option>
<option value="jpg" {{ if eq .Setting.BFormat "jpg" }}selected{{ end }}>
JPG</option>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- CORS and Rate Limit moved to Güvenlik tab -->
<h5 class="mt-4">Sosyal Medya</h5>
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Instagram</label>
<input type="text" class="form-control" name="instagram"
value="{{ .Setting.Instagram }}">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Twitter (X)</label>
<input type="text" class="form-control" name="x" value="{{ .Setting.X }}">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Facebook</label>
<input type="text" class="form-control" name="facebook" value="{{ .Setting.Facebook }}">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Whatsapp</label>
<input type="text" class="form-control" name="whatsapp" value="{{ .Setting.Whatsapp }}">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Linkedin</label>
<input type="text" class="form-control" name="linkedin" value="{{ .Setting.Linkedin }}">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Pinterest</label>
<input type="text" class="form-control" name="pinterest"
value="{{ .Setting.Pinterest }}">
</div>
</div>
<div class="mb-3 form-check form-switch">
<input class="form-check-input" type="checkbox" id="is_active" name="is_active" {{ if
.Setting.IsActive }}checked{{ end }}>
<label class="form-check-label" for="is_active">Site Aktif</label>
</div>
<button type="submit" class="btn btn-primary">Kaydet</button>
</form>
</div>
</div>
</div>
<!-- Security Tab (CORS / Rate Limits) -->
<div class="tab-pane fade" id="security" role="tabpanel">
<div class="card border-0 shadow-sm">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">CORS Yönetimi</h5>
<div class="btn-group">
<a href="/admin/content/settings" class="btn btn-outline-primary {{ if not .ShowDeleted }}active{{ end }}">Aktif</a>
<a href="/admin/content/settings?deleted=true" class="btn btn-outline-danger {{ if .ShowDeleted }}active{{ end }}">Silinenler</a>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<div class="card border p-3 mb-3">
<h6 class="mb-3">Whitelist (İzinli Origin'ler)</h6>
{{ if .EditWhitelist.ID }}
<form action="/admin/settings/cors/whitelist/{{ .EditWhitelist.ID }}/update" method="POST" class="mb-3">
<div class="input-group mb-2">
<input type="text" name="origin" class="form-control" placeholder="https://example.com" required value="{{ .EditWhitelist.Origin }}">
</div>
<div class="mb-2">
<input type="text" name="description" class="form-control" placeholder="Açıklama (opsiyonel)" value="{{ .EditWhitelist.Description }}">
</div>
<div class="form-check form-switch mb-2">
<input class="form-check-input" type="checkbox" id="wl_active" name="is_active" {{ if .EditWhitelist.IsActive }}checked{{ end }}>
<label class="form-check-label" for="wl_active">Aktif</label>
</div>
<div class="d-flex gap-2 justify-content-end">
<a href="/admin/content/settings" class="btn btn-sm btn-secondary">İptal</a>
<button class="btn btn-sm btn-primary" type="submit">Güncelle</button>
</div>
</form>
{{ else }}
<form action="/admin/settings/cors/whitelist/create" method="POST" class="mb-3">
<div class="input-group mb-2">
<input type="text" name="origin" class="form-control" placeholder="https://example.com" required>
</div>
<div class="mb-2">
<input type="text" name="description" class="form-control" placeholder="Açıklama (opsiyonel)">
</div>
<div class="d-flex gap-2 justify-content-end">
<button class="btn btn-sm btn-primary" type="submit">Ekle</button>
</div>
</form>
{{ end }}
<div>
<ul class="list-group list-group-flush">
{{ if .CorsWhitelist }}
{{ range .CorsWhitelist }}
<li class="list-group-item d-flex justify-content-between align-items-center">
<div>
<strong>{{ .Origin }}</strong>
<div class="small text-muted">{{ .Description }}</div>
</div>
<div class="btn-group">
{{ if $.ShowDeleted }}
<form action="/admin/settings/cors/whitelist/{{ .ID }}/restore" method="POST">
<button class="btn btn-sm btn-success">Geri Yükle</button>
</form>
{{ else }}
<a href="/admin/content/settings?edit_whitelist={{ .ID }}" class="btn btn-sm btn-outline-secondary">Düzenle</a>
<form action="/admin/settings/cors/whitelist/{{ .ID }}/delete" method="POST" onsubmit="return confirmDelete(event, this);">
<button class="btn btn-sm btn-outline-danger">Sil</button>
</form>
{{ end }}
</div>
</li>
{{ end }}
{{ else }}
<li class="list-group-item">Kayıt yok</li>
{{ end }}
</ul>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card border p-3 mb-3">
<h6 class="mb-3">Blacklist (Yasaklı Origin'ler)</h6>
{{ if .EditBlacklist.ID }}
<form action="/admin/settings/cors/blacklist/{{ .EditBlacklist.ID }}/update" method="POST" class="mb-3">
<div class="input-group mb-2">
<input type="text" name="origin" class="form-control" placeholder="https://bad.com" required value="{{ .EditBlacklist.Origin }}">
</div>
<div class="mb-2">
<input type="text" name="reason" class="form-control" placeholder="Sebep (opsiyonel)" value="{{ .EditBlacklist.Reason }}">
</div>
<div class="form-check form-switch mb-2">
<input class="form-check-input" type="checkbox" id="bl_active" name="is_active" {{ if .EditBlacklist.IsActive }}checked{{ end }}>
<label class="form-check-label" for="bl_active">Aktif</label>
</div>
<div class="d-flex gap-2 justify-content-end">
<a href="/admin/content/settings" class="btn btn-sm btn-secondary">İptal</a>
<button class="btn btn-sm btn-primary" type="submit">Güncelle</button>
</div>
</form>
{{ else }}
<form action="/admin/settings/cors/blacklist/create" method="POST" class="mb-3">
<div class="input-group mb-2">
<input type="text" name="origin" class="form-control" placeholder="https://bad.com" required>
</div>
<div class="mb-2">
<input type="text" name="reason" class="form-control" placeholder="Sebep (opsiyonel)">
</div>
<div class="d-flex gap-2 justify-content-end">
<button class="btn btn-sm btn-primary" type="submit">Ekle</button>
</div>
</form>
{{ end }}
<div>
<ul class="list-group list-group-flush">
{{ if .CorsBlacklist }}
{{ range .CorsBlacklist }}
<li class="list-group-item d-flex justify-content-between align-items-center">
<div>
<strong>{{ .Origin }}</strong>
<div class="small text-muted">{{ .Reason }}</div>
</div>
<div class="btn-group">
{{ if $.ShowDeleted }}
<form action="/admin/settings/cors/blacklist/{{ .ID }}/restore" method="POST">
<button class="btn btn-sm btn-success">Geri Yükle</button>
</form>
{{ else }}
<a href="/admin/content/settings?edit_blacklist={{ .ID }}" class="btn btn-sm btn-outline-secondary">Düzenle</a>
<form action="/admin/settings/cors/blacklist/{{ .ID }}/delete" method="POST" onsubmit="return confirmDelete(event, this);">
<button class="btn btn-sm btn-outline-danger">Sil</button>
</form>
{{ end }}
</div>
</li>
{{ end }}
{{ else }}
<li class="list-group-item">Kayıt yok</li>
{{ end }}
</ul>
</div>
</div>
</div>
</div>
<h5 class="mb-3">Rate Limit Ayarları</h5>
<div class="card border p-3 mb-3">
{{ if .EditRateLimit.ID }}
<form action="/admin/settings/rate-limit/{{ .EditRateLimit.ID }}/update" method="POST" class="row g-2 mb-3">
<div class="col-md-3">
<input type="text" name="name" class="form-control" placeholder="İsim (ör. login)" required value="{{ .EditRateLimit.Name }}">
</div>
<div class="col-md-3">
<input type="number" name="max_requests" class="form-control" placeholder="Max istek" required value="{{ .EditRateLimit.MaxRequests }}">
</div>
<div class="col-md-3">
<input type="number" name="window_seconds" class="form-control" placeholder="Pencere (s)" required value="{{ .EditRateLimit.WindowSeconds }}">
</div>
<div class="col-md-3 d-flex gap-2">
<a href="/admin/content/settings" class="btn btn-secondary">İptal</a>
<button class="btn btn-primary" type="submit">Güncelle</button>
</div>
</form>
{{ else }}
<form action="/admin/settings/rate-limit/create" method="POST" class="row g-2 mb-3">
<div class="col-md-3">
<input type="text" name="name" class="form-control" placeholder="İsim (ör. login)" required>
</div>
<div class="col-md-3">
<input type="number" name="max_requests" class="form-control" placeholder="Max istek" required>
</div>
<div class="col-md-3">
<input type="number" name="window_seconds" class="form-control" placeholder="Pencere (s)" required>
</div>
<div class="col-md-3 d-flex gap-2">
<button class="btn btn-primary" type="submit">Ekle</button>
</div>
</form>
{{ end }}
<div>
<ul class="list-group list-group-flush">
{{ if .RateLimits }}
{{ range .RateLimits }}
<li class="list-group-item d-flex justify-content-between align-items-center">
<div>
<strong>{{ .Name }}</strong>
<div class="small text-muted">{{ .MaxRequests }} req / {{ .WindowSeconds }}s</div>
</div>
<div class="btn-group">
{{ if $.ShowDeleted }}
<form action="/admin/settings/rate-limit/{{ .ID }}/restore" method="POST">
<button class="btn btn-sm btn-success">Geri Yükle</button>
</form>
{{ else }}
<a href="/admin/content/settings?edit_ratelimit={{ .ID }}" class="btn btn-sm btn-outline-secondary">Düzenle</a>
<form action="/admin/settings/rate-limit/{{ .ID }}/delete" method="POST" onsubmit="return confirmDelete(event, this);">
<button class="btn btn-sm btn-outline-danger">Sil</button>
</form>
{{ end }}
</div>
</li>
{{ end }}
{{ else }}
<li class="list-group-item">Kayıt yok</li>
{{ end }}
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Hero Management Tab -->
<div class="tab-pane fade" id="hero" role="tabpanel">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="btn-group" role="group">
<a href="/admin/content/settings"
class="btn btn-outline-primary {{ if not .ShowDeleted }}active{{ end }}">Aktif</a>
<a href="/admin/content/settings?deleted=true"
class="btn btn-outline-danger {{ if .ShowDeleted }}active{{ end }}">Silinenler</a>
</div>
<a href="/admin/heroes/new" class="btn btn-primary">
<i class="bi bi-plus-lg"></i> Yeni Banner Ekle
</a>
</div>
<div class="card border-0 shadow-sm">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Görsel/Renk</th>
<th>Başlık</th>
<th>Alt Başlık</th>
<th>Durum</th>
<th>İşlemler</th>
</tr>
</thead>
<tbody>
{{ range .Heroes }}
<tr>
<td>
<div style="width: 50px; height: 30px; background-color: {{ .Color }}; border-radius: 4px; border: 1px solid #ddd;"
title="{{ .Color }}"></div>
</td>
<td>{{ .Title }}</td>
<td>{{ .Text1 }}</td>
<td>
{{ if .IsActive }}
<span class="badge bg-success">Aktif</span>
{{ else }}
<span class="badge bg-secondary">Pasif</span>
{{ end }}
</td>
<td>
<div class="d-flex gap-2">
{{ if $.ShowDeleted }}
<form action="/admin/heroes/{{ .ID }}/restore" method="POST"
onsubmit="return confirmRestore(event, this);">
<button type="submit" class="btn btn-sm btn-success" title="Geri Yükle">
<i class="bi bi-arrow-counterclockwise"></i>
</button>
</form>
{{ else }}
<a href="/admin/heroes/{{ .ID }}/edit"
class="btn btn-sm btn-outline-secondary" title="Düzenle">
<i class="bi bi-pencil"></i>
</a>
<form action="/admin/heroes/{{ .ID }}/delete" method="POST"
onsubmit="return confirmDelete(event, this);">
<button type="submit" class="btn btn-sm btn-outline-danger" title="Sil">
<i class="bi bi-trash"></i>
</button>
</form>
{{ end }}
</div>
</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Simple tab state preservation using URL hash or local storage is complex with HTMX
// For now, let's just make sure tabs work via Bootstrap JS
// The tabs might reset to first tab on HTMX swap/update if we are not careful.
// Since we reload the whole #settings-container (this file) on update, tabs will reset.
// We can add a small script to check URL param or sessionStorage to activate correct tab.
(function(){
const STORAGE_KEY = 'admin.settings.activeTab';
// Save active tab id when clicked
document.querySelectorAll('#settingsTab button[data-bs-toggle="tab"]').forEach(btn => {
btn.addEventListener('shown.bs.tab', (e) => {
try { sessionStorage.setItem(STORAGE_KEY, e.target.id); } catch(e){}
});
});
// Restore on load
function restoreTab(){
try{
const id = sessionStorage.getItem(STORAGE_KEY);
if(id){
const btn = document.getElementById(id);
if(btn){
const tab = new bootstrap.Tab(btn);
tab.show();
}
}
}catch(e){}
}
document.addEventListener('DOMContentLoaded', restoreTab);
// HTMX: after swapping settings container, restore the tab
document.addEventListener('htmx:afterSwap', function(evt){
if(evt.detail.target && evt.detail.target.id === undefined){ return; }
const tgt = evt.detail.target;
if(tgt && (tgt.id === 'users-container' || tgt.id === 'main-content' || tgt.id === 'modal-area')){
// not our container
return;
}
// if settings container was swapped, restore
if(tgt && tgt.closest && tgt.closest('.container-fluid') && tgt.closest('.container-fluid').querySelector('#settingsTab')){
restoreTab();
}
});
})();
</script>