first commit
This commit is contained in:
75
views/admin/pages/category_form.html
Normal file
75
views/admin/pages/category_form.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<div class="container-fluid">
|
||||
<h2>{{ if .IsEdit }}Kategori Düzenle{{ else }}Yeni Kategori{{ end }}</h2>
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
{{ if .IsEdit }}
|
||||
<form action="/admin/categories/{{ .Category.ID }}/update" method="POST">
|
||||
{{ else }}
|
||||
<form action="/admin/categories/create" method="POST">
|
||||
{{ end }}
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Başlık</label>
|
||||
<input id="category-title" type="text" name="title" class="form-control" value="{{ if .IsEdit }}{{ .Category.Title }}{{ end }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Slug</label>
|
||||
<div class="input-group">
|
||||
<input id="category-slug" type="text" name="slug" class="form-control" value="{{ if .IsEdit }}{{ .Category.Slug }}{{ end }}" required>
|
||||
<button id="reset-slug" type="button" class="btn btn-outline-secondary">Oluştur</button>
|
||||
</div>
|
||||
<div class="form-text">Slug otomatik oluşturulur; düzenleyebilirsiniz.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Açıklama</label>
|
||||
<textarea name="description" class="form-control">{{ if .IsEdit }}{{ .Category.Description }}{{ end }}</textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Parent</label>
|
||||
<select name="parent_id" class="form-select">
|
||||
<option value="">-- Yok --</option>
|
||||
{{ range .Parents }}
|
||||
<option value="{{ .ID }}" {{ if $.IsEdit }}{{ if eq $.ParentID .ID }}selected{{ end }}{{ end }}>{{ .Title }}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/admin/content/categories" class="btn btn-secondary">Geri</a>
|
||||
<button class="btn btn-primary" type="submit">Kaydet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function(){
|
||||
function slugify(s){
|
||||
if(!s) return '';
|
||||
s = s.trim();
|
||||
// Turkish char replacements
|
||||
s = s.replace(/ç/g,'c').replace(/Ç/g,'c')
|
||||
.replace(/ğ/g,'g').replace(/Ğ/g,'g')
|
||||
.replace(/ı/g,'i').replace(/İ/g,'i')
|
||||
.replace(/ö/g,'o').replace(/Ö/g,'o')
|
||||
.replace(/ş/g,'s').replace(/Ş/g,'s')
|
||||
.replace(/ü/g,'u').replace(/Ü/g,'u');
|
||||
// remove diacritics
|
||||
s = s.normalize('NFKD').replace(/\p{M}/gu, '');
|
||||
s = s.toLowerCase();
|
||||
s = s.replace(/[^a-z0-9]+/g,'-');
|
||||
s = s.replace(/^-+|-+$/g,'');
|
||||
return s;
|
||||
}
|
||||
|
||||
const titleEl = document.getElementById('category-title');
|
||||
const slugEl = document.getElementById('category-slug');
|
||||
const resetBtn = document.getElementById('reset-slug');
|
||||
if(!titleEl || !slugEl) return;
|
||||
|
||||
let manual = false;
|
||||
slugEl.addEventListener('input', ()=> { manual = true; });
|
||||
titleEl.addEventListener('input', ()=> {
|
||||
if(!manual){ slugEl.value = slugify(titleEl.value); }
|
||||
});
|
||||
resetBtn.addEventListener('click', ()=> { slugEl.value = slugify(titleEl.value); manual = false; });
|
||||
})();
|
||||
</script>
|
||||
113
views/admin/pages/hero_form.html
Normal file
113
views/admin/pages/hero_form.html
Normal file
@@ -0,0 +1,113 @@
|
||||
{{ define "admin/pages/hero_form" }}
|
||||
<div class="container-fluid">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2>{{ if .IsEdit }}Banner Düzenle{{ else }}Yeni Banner Ekle{{ end }}</h2>
|
||||
<a href="/admin/content/settings" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Geri Dön
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<form action="{{ if .IsEdit }}/admin/heroes/{{ .Hero.ID }}/update{{ else }}/admin/heroes/create{{ end }}"
|
||||
method="POST" enctype="multipart/form-data">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="title" class="form-label">Başlık</label>
|
||||
<input type="text" class="form-control" id="title" name="title" value="{{ .Hero.Title }}">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="color" class="form-label">Renk Kodu (örn. #ffffff)</label>
|
||||
<div class="input-group">
|
||||
<input type="color" class="form-control form-control-color" id="colorPicker"
|
||||
value="{{ .Hero.Color }}" title="Renk seç">
|
||||
<input type="text" class="form-control" id="color" name="color" value="{{ .Hero.Color }}"
|
||||
placeholder="#000000" required>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById('colorPicker').addEventListener('input', function () {
|
||||
document.getElementById('color').value = this.value;
|
||||
});
|
||||
document.getElementById('color').addEventListener('input', function () {
|
||||
document.getElementById('colorPicker').value = this.value;
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="text1" class="form-label">Alt Başlık (Text1)</label>
|
||||
<input type="text" class="form-control" id="text1" name="text1" value="{{ .Hero.Text1 }}">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="text2" class="form-label">Açıklama (Text2)</label>
|
||||
<input type="text" class="form-control" id="text2" name="text2" value="{{ .Hero.Text2 }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="text4" class="form-label">Buton Metni (Text4)</label>
|
||||
<input type="text" class="form-control" id="text4" name="text4" value="{{ .Hero.Text4 }}">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="text5" class="form-label">Buton Linki (Text5)</label>
|
||||
<input type="text" class="form-control" id="text5" name="text5" value="{{ .Hero.Text5 }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="width" class="form-label">Genişlik (px)</label>
|
||||
<input type="number" class="form-control" id="width" name="width" value="{{ .Hero.Width }}">
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="height" class="form-label">Yükseklik (px)</label>
|
||||
<input type="number" class="form-control" id="height" name="height" value="{{ .Hero.Height }}">
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="quality" class="form-label">Kalite (1-100)</label>
|
||||
<input type="number" class="form-control" id="quality" name="quality"
|
||||
value="{{ .Hero.Quality }}" placeholder="80">
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="format" class="form-label">Format</label>
|
||||
<select class="form-select" id="format" name="format">
|
||||
<option value="avif" {{ if eq .Hero.Format "avif" }}selected{{ end }}>AVIF (Önerilen)
|
||||
</option>
|
||||
<option value="webp" {{ if eq .Hero.Format "webp" }}selected{{ end }}>WebP</option>
|
||||
<option value="jpg" {{ if eq .Hero.Format "jpg" }}selected{{ end }}>JPG</option>
|
||||
<option value="png" {{ if eq .Hero.Format "png" }}selected{{ end }}>PNG</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="image" class="form-label">Resim Yükle</label>
|
||||
<input type="file" class="form-control" id="image" name="image" accept="image/*">
|
||||
{{ if .Hero.Image }}
|
||||
<div class="mt-2">
|
||||
<p>Mevcut Resim: <a href="{{ .Hero.Image }}" target="_blank">{{ .Hero.Image }}</a></p>
|
||||
<img src="{{ .Hero.Image }}" alt="Current Hero Image"
|
||||
style="max-height: 100px; border-radius: 5px;">
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="is_active" name="is_active" {{ if or (not
|
||||
.IsEdit) .Hero.IsActive }}checked{{ end }}>
|
||||
<label class="form-check-label" for="is_active">Aktif</label>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="/admin/content/settings" class="btn btn-light">İptal</a>
|
||||
<button type="submit" class="btn btn-primary">Kaydet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
161
views/admin/pages/post_form.html
Normal file
161
views/admin/pages/post_form.html
Normal file
@@ -0,0 +1,161 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h4>{{ if .IsEdit }}Yazıyı Düzenle{{ else }}Yeni Yazı{{ end }}</h4>
|
||||
<form action="{{ if .IsEdit }}/admin/posts/{{ .Post.ID }}/update{{ else }}/admin/posts/create{{ end }}" method="POST" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Başlık</label>
|
||||
<input type="text" name="title" class="form-control" value="{{ if .IsEdit }}{{ .Post.Title }}{{ end }}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Slug</label>
|
||||
<input type="text" name="slug" class="form-control" value="{{ if .IsEdit }}{{ .Post.Slug }}{{ end }}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">İçerik</label>
|
||||
<textarea id="contentInput" name="content" rows="8" class="form-control d-none">{{ if .IsEdit }}{{ .Post.Content }}{{ end }}</textarea>
|
||||
<div id="quillEditor" style="min-height:300px;border:1px solid #ddd;border-radius:4px;">
|
||||
<!-- initial content will be loaded from the hidden textarea by JS -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kategoriler (Ctrl/Tık birden çok seçim)</label>
|
||||
<select name="category_ids" class="form-select" multiple>
|
||||
{{ range .Categories }}
|
||||
<option value="{{ .ID }}" {{ if $.IsEdit }}{{ range $.Post.Categories }}{{ if eq .ID $.ID }}selected{{ end }}{{ end }}{{ end }}>{{ .Title }}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Taglar (Ctrl/Tık birden çok seçim)</label>
|
||||
<select name="tag_ids" class="form-select" multiple>
|
||||
{{ range .Tags }}
|
||||
<option value="{{ .ID }}" {{ if $.IsEdit }}{{ range $.Post.Tags }}{{ if eq .ID $.ID }}selected{{ end }}{{ end }}{{ end }}>{{ .Name }}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Görsel (Tek dosya)</label>
|
||||
<input type="file" name="image" class="form-control" accept="image/*">
|
||||
<div class="mt-2">
|
||||
<img id="imagePreview" src="" alt="preview" class="rounded" style="display:none;width:150px;height:150px;object-fit:cover;border:1px solid #ddd;">
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col-3">
|
||||
<label class="form-label">Genişlik</label>
|
||||
<input type="number" name="width" class="form-control" value="{{ if .IsEdit }}{{ .Post.Width }}{{ else }}1920{{ end }}">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label class="form-label">Yükseklik</label>
|
||||
<input type="number" name="height" class="form-control" value="{{ if .IsEdit }}{{ .Post.Height }}{{ else }}1080{{ end }}">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label class="form-label">Kalite</label>
|
||||
<input type="number" name="quality" class="form-control" value="{{ if .IsEdit }}{{ .Post.Quality }}{{ else }}80{{ end }}">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label class="form-label">Format</label>
|
||||
<select name="format" class="form-select">
|
||||
<option value="avif" {{ if .IsEdit }}{{ if eq .Post.Format "avif" }}selected{{ end }}{{ end }}>AVIF</option>
|
||||
<option value="webp" {{ if .IsEdit }}{{ if eq .Post.Format "webp" }}selected{{ end }}{{ end }}>WebP</option>
|
||||
<option value="jpg" {{ if .IsEdit }}{{ if eq .Post.Format "jpg" }}selected{{ end }}{{ end }}>JPG</option>
|
||||
<option value="png" {{ if .IsEdit }}{{ if eq .Post.Format "png" }}selected{{ end }}{{ end }}>PNG</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 d-flex gap-2">
|
||||
<button class="btn btn-primary">{{ if .IsEdit }}Güncelle{{ else }}Oluştur{{ end }}</button>
|
||||
<a href="/admin/content/posts" class="btn btn-outline-secondary" hx-get="/admin/content/posts" hx-target="#main-content" hx-push-url="true">İptal</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h6>Mevcut Görsel</h6>
|
||||
{{ if .IsEdit }}
|
||||
{{ if .Post.Images }}
|
||||
<div class="small text-muted">Görsel yolları: {{ .Post.Images }}</div>
|
||||
{{ else }}
|
||||
<div class="text-muted small">Henüz görsel yüklenmedi.</div>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<div class="text-muted small">Henüz görsel yüklenmedi.</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
const fileInput = document.querySelector('input[name="image"]');
|
||||
const preview = document.getElementById('imagePreview');
|
||||
// show existing image if provided by controller
|
||||
const firstImage = '{{ .FirstImage }}';
|
||||
if (firstImage && firstImage !== '') {
|
||||
preview.src = firstImage;
|
||||
preview.style.display = 'block';
|
||||
}
|
||||
if (fileInput) {
|
||||
fileInput.addEventListener('change', function(e){
|
||||
const f = this.files && this.files[0];
|
||||
if (!f) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(ev){
|
||||
preview.src = ev.target.result;
|
||||
preview.style.display = 'block';
|
||||
}
|
||||
reader.readAsDataURL(f);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Quill: load local CSS and JS; initialize editor and sync to textarea on submit -->
|
||||
<link href="/assets/quill/dist/quill.snow.css" rel="stylesheet">
|
||||
<script src="/assets/quill/dist/quill.js"></script>
|
||||
<script>
|
||||
(function initQuill(){
|
||||
try {
|
||||
var textarea = document.getElementById('contentInput');
|
||||
var editorContainer = document.getElementById('quillEditor');
|
||||
if (!editorContainer) return;
|
||||
// Avoid double-init
|
||||
if (editorContainer.__quillInitialized) return;
|
||||
// Initialize Quill
|
||||
var quill = new Quill(editorContainer, {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: {
|
||||
container: [['bold', 'italic', 'underline'], [{ 'list': 'ordered'}, { 'list': 'bullet' }], ['link', 'image'], ['code-block'], ['clean']],
|
||||
handlers: {
|
||||
image: function() {
|
||||
try { window.showImageModal(quill); } catch(e) { console.error('open image modal failed', e); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
editorContainer.__quillInitialized = true;
|
||||
// Load initial content from textarea (if any)
|
||||
if (textarea && textarea.value && textarea.value.trim() !== '') {
|
||||
quill.clipboard.dangerouslyPasteHTML(textarea.value);
|
||||
}
|
||||
// On form submit, copy html to hidden textarea
|
||||
var form = textarea && textarea.form;
|
||||
if (form) {
|
||||
form.addEventListener('submit', function(e){
|
||||
textarea.value = quill.root.innerHTML;
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Quill init error:', err);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
87
views/admin/pages/product_category_form.html
Normal file
87
views/admin/pages/product_category_form.html
Normal file
@@ -0,0 +1,87 @@
|
||||
<div class="container-fluid">
|
||||
<h2>{{ if .IsEdit }}Ürün Kategorisi Düzenle{{ else }}Yeni Ürün Kategorisi{{ end }}</h2>
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
{{ if .IsEdit }}
|
||||
<form action="/admin/product-categories/{{ .Category.ID }}/update" method="POST">
|
||||
{{ else }}
|
||||
<form action="/admin/product-categories/create" method="POST">
|
||||
{{ end }}
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Başlık</label>
|
||||
<input id="category-title" type="text" name="title" class="form-control"
|
||||
value="{{ if .IsEdit }}{{ .Category.Title }}{{ end }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Slug</label>
|
||||
<div class="input-group">
|
||||
<input id="category-slug" type="text" name="slug" class="form-control"
|
||||
value="{{ if .IsEdit }}{{ .Category.Slug }}{{ end }}">
|
||||
<button id="reset-slug" type="button" class="btn btn-outline-secondary">Oluştur</button>
|
||||
</div>
|
||||
<div class="form-text">Boş bırakılırsa başlıktan otomatik oluşturulur; düzenleyebilirsiniz.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Açıklama</label>
|
||||
<textarea name="description"
|
||||
class="form-control">{{ if .IsEdit }}{{ .Category.Description }}{{ end }}</textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Anahtar Kelimeler (Keywords)</label>
|
||||
<input type="text" name="keywords" class="form-control"
|
||||
value="{{ if .IsEdit }}{{ .Category.Keywords }}{{ end }}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Üst Kategori</label>
|
||||
<select name="parent_id" class="form-select">
|
||||
<option value="">-- Yok --</option>
|
||||
{{ range .Parents }}
|
||||
{{ $isSelected := false }}
|
||||
{{ if $.IsEdit }}{{ if $.Category.ParentID }}{{ if eq (print $.Category.ParentID) (print
|
||||
.ID) }}{{ $isSelected = true }}{{ end }}{{ end }}{{ end }}
|
||||
<option value="{{ .ID }}" {{ if $isSelected }}selected{{ end }}>{{ .Title }}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/admin/content/product-categories" class="btn btn-secondary">Geri</a>
|
||||
<button class="btn btn-primary" type="submit">Kaydet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
function slugify(s) {
|
||||
if (!s) return '';
|
||||
s = s.trim();
|
||||
// Turkish char replacements
|
||||
s = s.replace(/ç/g, 'c').replace(/Ç/g, 'c')
|
||||
.replace(/ğ/g, 'g').replace(/Ğ/g, 'g')
|
||||
.replace(/ı/g, 'i').replace(/İ/g, 'i')
|
||||
.replace(/ö/g, 'o').replace(/Ö/g, 'o')
|
||||
.replace(/ş/g, 's').replace(/Ş/g, 's')
|
||||
.replace(/ü/g, 'u').replace(/Ü/g, 'u');
|
||||
// remove diacritics
|
||||
s = s.normalize('NFKD').replace(/\p{M}/gu, '');
|
||||
s = s.toLowerCase();
|
||||
s = s.replace(/[^a-z0-9]+/g, '-');
|
||||
s = s.replace(/^-+|-+$/g, '');
|
||||
return s;
|
||||
}
|
||||
|
||||
const titleEl = document.getElementById('category-title');
|
||||
const slugEl = document.getElementById('category-slug');
|
||||
const resetBtn = document.getElementById('reset-slug');
|
||||
if (!titleEl || !slugEl) return;
|
||||
|
||||
let manual = false;
|
||||
slugEl.addEventListener('input', () => { manual = true; });
|
||||
titleEl.addEventListener('input', () => {
|
||||
if (!manual) { slugEl.value = slugify(titleEl.value); }
|
||||
});
|
||||
resetBtn.addEventListener('click', () => { slugEl.value = slugify(titleEl.value); manual = false; });
|
||||
})();
|
||||
</script>
|
||||
191
views/admin/pages/product_form.html
Normal file
191
views/admin/pages/product_form.html
Normal file
@@ -0,0 +1,191 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h4>{{ if .IsEdit }}Ürünü Düzenle{{ else }}Yeni Ürün{{ end }}</h4>
|
||||
<form
|
||||
action="{{ if .IsEdit }}/admin/products/{{ .Product.ID }}/update{{ else }}/admin/products/create{{ end }}"
|
||||
method="POST" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Başlık</label>
|
||||
<input type="text" name="title" class="form-control"
|
||||
value="{{ if .IsEdit }}{{ .Product.Title }}{{ end }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Slug</label>
|
||||
<input type="text" name="slug" class="form-control"
|
||||
value="{{ if .IsEdit }}{{ .Product.Slug }}{{ end }}"
|
||||
placeholder="Boş bırakılırsa başlıktan otomatik oluşturulur">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">İçerik</label>
|
||||
<textarea id="contentInput" name="content" rows="8"
|
||||
class="form-control d-none">{{ if .IsEdit }}{{ .Product.Content }}{{ end }}</textarea>
|
||||
<div id="quillEditor" style="min-height:300px;border:1px solid #ddd;border-radius:4px;">
|
||||
<!-- initial content will be loaded from the hidden textarea by JS -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kategoriler (Ctrl/Tık birden çok seçim)</label>
|
||||
<select name="categories" class="form-select" multiple>
|
||||
{{ range .Categories }}
|
||||
<option value="{{ .ID }}" {{ if $.IsEdit }}{{ range $.Product.Categories }}{{ if eq .ID
|
||||
$.ID }}selected{{ end }}{{ end }}{{ end }}>{{ .Title }}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Etiketler (Ctrl/Tık birden çok seçim)</label>
|
||||
<select name="tags" class="form-select" multiple>
|
||||
{{ range .Tags }}
|
||||
<option value="{{ .ID }}" {{ if $.IsEdit }}{{ range $.Product.Tags }}{{ if eq .ID $.ID
|
||||
}}selected{{ end }}{{ end }}{{ end }}>{{ .Name }}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Ana Görsel (Tek dosya)</label>
|
||||
<input type="file" name="image" class="form-control" accept="image/*">
|
||||
<div class="mt-2">
|
||||
<img id="imagePreview" src="" alt="preview" class="rounded"
|
||||
style="display:none;width:150px;height:150px;object-fit:cover;border:1px solid #ddd;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Fiyat</label>
|
||||
<input type="number" step="0.01" name="price" class="form-control"
|
||||
value="{{ if .IsEdit }}{{ .Product.Price }}{{ else }}0.00{{ end }}" required>
|
||||
</div>
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-3">
|
||||
<label class="form-label">Genişlik</label>
|
||||
<input type="number" name="width" class="form-control"
|
||||
value="{{ if .IsEdit }}{{ .Product.Width }}{{ end }}" placeholder="Örn: 800">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label class="form-label">Yükseklik</label>
|
||||
<input type="number" name="height" class="form-control"
|
||||
value="{{ if .IsEdit }}{{ .Product.Height }}{{ end }}" placeholder="Örn: 600">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label class="form-label">Kalite</label>
|
||||
<input type="number" name="quality" class="form-control"
|
||||
value="{{ if .IsEdit }}{{ .Product.Quality }}{{ else }}80{{ end }}">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label class="form-label">Format</label>
|
||||
<select name="format" class="form-select">
|
||||
<option value="avif" {{ if .IsEdit }}{{ if eq .Product.Format "avif" }}selected{{
|
||||
end }}{{ end }}>AVIF</option>
|
||||
<option value="webp" {{ if .IsEdit }}{{ if eq .Product.Format "webp" }}selected{{
|
||||
end }}{{ end }}>WebP</option>
|
||||
<option value="jpg" {{ if .IsEdit }}{{ if eq .Product.Format "jpg" }}selected{{ end
|
||||
}}{{ end }}>JPG</option>
|
||||
<option value="png" {{ if .IsEdit }}{{ if eq .Product.Format "png" }}selected{{ end
|
||||
}}{{ end }}>PNG</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">{{ if .IsEdit }}Güncelle{{ else }}Oluştur{{
|
||||
end }}</button>
|
||||
<a href="/admin/content/products" class="btn btn-outline-secondary"
|
||||
hx-get="/admin/content/products" hx-target="#main-content" hx-push-url="true">İptal</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-body">
|
||||
<h6>Mevcut Ana Görsel</h6>
|
||||
{{ if .IsEdit }}
|
||||
{{ if .FirstImage }}
|
||||
<div class="mb-2">
|
||||
<img src="{{ .FirstImage }}" class="img-fluid rounded" alt="Mevcut Görsel">
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="text-muted small">Henüz görsel yüklenmedi.</div>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<div class="text-muted small">Yeni ürün için henüz görsel yok.</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h6>Bilgi</h6>
|
||||
<p class="small text-muted mb-0">Ürün eklerken Kategori ve Etiket seçimi yapabilirsiniz.
|
||||
Seçeneklerin dolu olması için ilgili menülerden kategori ve etiket verisi girmeyi unutmayın.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const fileInput = document.querySelector('input[name="image"]');
|
||||
const preview = document.getElementById('imagePreview');
|
||||
// show existing image if provided by controller but not rendered inline above
|
||||
const firstImage = '{{ .FirstImage }}';
|
||||
if (firstImage && firstImage !== '') {
|
||||
preview.src = firstImage;
|
||||
preview.style.display = 'block';
|
||||
}
|
||||
if (fileInput) {
|
||||
fileInput.addEventListener('change', function (e) {
|
||||
const f = this.files && this.files[0];
|
||||
if (!f) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (ev) {
|
||||
preview.src = ev.target.result;
|
||||
preview.style.display = 'block';
|
||||
}
|
||||
reader.readAsDataURL(f);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Quill: load local CSS and JS; initialize editor and sync to textarea on submit -->
|
||||
<link href="/assets/quill/dist/quill.snow.css" rel="stylesheet">
|
||||
<script src="/assets/quill/dist/quill.js"></script>
|
||||
<script>
|
||||
(function initQuill() {
|
||||
try {
|
||||
var textarea = document.getElementById('contentInput');
|
||||
var editorContainer = document.getElementById('quillEditor');
|
||||
if (!editorContainer) return;
|
||||
if (editorContainer.__quillInitialized) return;
|
||||
var quill = new Quill(editorContainer, {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: {
|
||||
container: [['bold', 'italic', 'underline'], [{ 'list': 'ordered' }, { 'list': 'bullet' }], ['link', 'image'], ['code-block'], ['clean']],
|
||||
handlers: {
|
||||
image: function () {
|
||||
try { window.showImageModal(quill); } catch (e) { console.error('open image modal failed', e); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
editorContainer.__quillInitialized = true;
|
||||
if (textarea && textarea.value && textarea.value.trim() !== '') {
|
||||
quill.clipboard.dangerouslyPasteHTML(textarea.value);
|
||||
}
|
||||
var form = textarea && textarea.form;
|
||||
if (form) {
|
||||
form.addEventListener('submit', function (e) {
|
||||
textarea.value = quill.root.innerHTML;
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Quill init error:', err);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
22
views/admin/pages/product_tag_form.html
Normal file
22
views/admin/pages/product_tag_form.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<div class="container-fluid">
|
||||
<h2>{{ if .IsEdit }}Ürün Etiketi Düzenle{{ else }}Yeni Ürün Etiketi{{ end }}</h2>
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
{{ if .IsEdit }}
|
||||
<form action="/admin/product-tags/{{ .Tag.ID }}/update" method="POST">
|
||||
{{ else }}
|
||||
<form action="/admin/product-tags/create" method="POST">
|
||||
{{ end }}
|
||||
<div class="mb-3">
|
||||
<label class="form-label">İsim</label>
|
||||
<input type="text" name="name" class="form-control"
|
||||
value="{{ if .IsEdit }}{{ .Tag.Name }}{{ end }}" required>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/admin/content/product-tags" class="btn btn-secondary">Geri</a>
|
||||
<button class="btn btn-primary" type="submit">Kaydet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
21
views/admin/pages/tag_form.html
Normal file
21
views/admin/pages/tag_form.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<div class="container-fluid">
|
||||
<h2>{{ if .IsEdit }}Tag Düzenle{{ else }}Yeni Tag{{ end }}</h2>
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
{{ if .IsEdit }}
|
||||
<form action="/admin/tags/{{ .Tag.ID }}/update" method="POST">
|
||||
{{ else }}
|
||||
<form action="/admin/tags/create" method="POST">
|
||||
{{ end }}
|
||||
<div class="mb-3">
|
||||
<label class="form-label">İsim</label>
|
||||
<input type="text" name="name" class="form-control" value="{{ if .IsEdit }}{{ .Tag.Name }}{{ end }}" required>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/admin/content/tags" class="btn btn-secondary">Geri</a>
|
||||
<button class="btn btn-primary" type="submit">Kaydet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
79
views/admin/pages/user_form.html
Normal file
79
views/admin/pages/user_form.html
Normal file
@@ -0,0 +1,79 @@
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8 col-lg-6">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0">{{ if .IsEdit }}Kullanıcı Düzenle{{ else }}Yeni Kullanıcı Oluştur{{ end }}</h5>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<form
|
||||
action="{{ if .IsEdit }}/admin/users/{{ .User.ID }}/update{{ else }}/admin/users/create{{ end }}"
|
||||
method="POST" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Kullanıcı Adı</label>
|
||||
<input type="text" class="form-control" id="username" name="username"
|
||||
value="{{ if .IsEdit }}{{ .User.UserName }}{{ end }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">E-posta</label>
|
||||
<input type="email" class="form-control" id="email" name="email"
|
||||
value="{{ if .IsEdit }}{{ .User.Email }}{{ end }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Şifre {{ if .IsEdit }}<small
|
||||
class="text-muted">(Boş bırakılırsa değişmez)</small>{{ end }}</label>
|
||||
<input type="password" class="form-control" id="password" name="password" {{ if not .IsEdit
|
||||
}}required{{ end }}>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="is_admin" name="is_admin" {{ if .IsEdit
|
||||
}}{{ if isTrue .User.IsAdmin }}checked{{ end }}{{ end }}>
|
||||
<label class="form-check-label" for="is_admin">
|
||||
Yönetici (Admin)
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="email_verified" name="email_verified" {{
|
||||
if .IsEdit }}{{ if isTrue .User.EmailVerified }}checked{{ end }}{{ end }}>
|
||||
<label class="form-check-label" for="email_verified">
|
||||
E-posta Doğrulanmış
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Profile fields -->
|
||||
<div class="mb-3">
|
||||
<label for="first_name" class="form-label">İsim</label>
|
||||
<input type="text" class="form-control" id="first_name" name="first_name"
|
||||
value="{{ if .IsEdit }}{{ if .User.Profile }}{{ range .User.Profile }}{{ .FirstName }}{{ end }}{{ end }}{{ end }}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="last_name" class="form-label">Soyisim</label>
|
||||
<input type="text" class="form-control" id="last_name" name="last_name"
|
||||
value="{{ if .IsEdit }}{{ if .User.Profile }}{{ range .User.Profile }}{{ .LastName }}{{ end }}{{ end }}{{ end }}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Avatar</label>
|
||||
<input type="file" class="form-control" name="avatar" accept="image/*">
|
||||
{{ if .IsEdit }}
|
||||
{{ if .User.Profile }}
|
||||
{{ range .User.Profile }}
|
||||
{{ if .AvatarURL }}
|
||||
<div class="mt-2">
|
||||
<img src="{{ .AvatarURL }}" width="64" height="64" class="rounded-circle">
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="/admin/content/users" class="btn btn-secondary">İptal</a>
|
||||
<button type="submit" class="btn btn-primary">Kaydet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user