This commit is contained in:
ilkeral
2025-07-21 13:49:36 +03:00
commit 342f1314c7
57 changed files with 9297 additions and 0 deletions

View File

@ -0,0 +1,467 @@
{% extends 'ssh_manager/base.html' %}
{% load static %}
{% block content %}
<style>
.customer-card {
background: #23272b;
border: 1px solid #333;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1rem;
transition: all 0.3s ease;
}
.customer-card:hover {
border-color: #4fc3f7;
box-shadow: 0 2px 8px rgba(79, 195, 247, 0.1);
}
.customer-type-badge {
font-size: 0.8rem;
padding: 0.25rem 0.75rem;
border-radius: 12px;
}
.customer-projects {
font-size: 0.9rem;
color: #bbb;
}
.action-buttons {
display: flex;
gap: 0.5rem;
justify-content: flex-end;
}
</style>
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h3>Müşteri Yönetimi
{% if filter_type == 'individual' %}
<small class="text-muted">- Bireysel Müşteriler</small>
{% elif filter_type == 'corporate' %}
<small class="text-muted">- Kurumsal Müşteriler</small>
{% endif %}
</h3>
<small class="text-muted">Bireysel ve kurumsal müşteri profilleri</small>
</div>
<div class="d-flex gap-2">
<input type="text" id="customerSearch" class="form-control" style="max-width: 250px;" placeholder="Müşteri ara...">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#customerModal" onclick="resetCustomerForm()">
<i class="bi bi-plus-circle"></i>
</button>
</div>
</div>
<div class="row">
{% for customer in customers %}
<div class="col-md-6 col-lg-4 customer-item" data-customer="{{ customer.get_display_name|lower }}">
<div class="customer-card">
<div class="d-flex justify-content-between align-items-start mb-2">
<div>
<h5 class="mb-1">{{ customer.get_display_name }}</h5>
<span class="customer-type-badge badge {% if customer.customer_type == 'corporate' %}bg-info{% else %}bg-success{% endif %}">
{% if customer.customer_type == 'corporate' %}Kurumsal{% else %}Bireysel{% endif %}
</span>
</div>
<div class="action-buttons">
<button class="btn btn-sm btn-outline-info" onclick="editCustomer({{ customer.id }})" title="Düzenle">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteCustomer({{ customer.id }})" title="Sil">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
<div class="customer-info">
{% if customer.email %}
<div class="mb-1">
<i class="bi bi-envelope me-1"></i>
<small>{{ customer.email }}</small>
</div>
{% endif %}
{% if customer.phone %}
<div class="mb-1">
<i class="bi bi-telephone me-1"></i>
<small>{{ customer.phone }}</small>
</div>
{% endif %}
{% if customer.customer_type == 'corporate' and customer.tax_number %}
<div class="mb-1">
<i class="bi bi-building me-1"></i>
<small>Vergi No: {{ customer.tax_number }}</small>
</div>
{% endif %}
</div>
<div class="customer-projects mt-3">
<i class="bi bi-folder me-1"></i>
<small>{{ customer.project_set.count }} proje</small>
{% if customer.notes %}
<div class="mt-2">
<i class="bi bi-chat-text me-1"></i>
<small class="text-muted">{{ customer.notes|truncatechars:50 }}</small>
</div>
{% endif %}
</div>
</div>
</div>
{% empty %}
<div class="col-12">
<div class="text-center text-muted py-5">
<i class="bi bi-people" style="font-size: 3rem; opacity: 0.3;"></i>
<p class="mt-2">Henüz müşteri kaydı bulunmuyor</p>
</div>
</div>
{% endfor %}
</div>
<!-- Müşteri Modal -->
<div class="modal fade" id="customerModal" tabindex="-1" aria-labelledby="customerModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form id="customerForm">
<div class="modal-header">
<h5 class="modal-title" id="customerModalLabel">Yeni Müşteri Ekle</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="customerId" name="customerId">
<!-- Müşteri Tipi Seçimi -->
<div class="mb-4">
<label class="form-label fw-bold">Müşteri Tipi</label>
<div class="d-flex gap-4 mt-2">
<div class="form-check">
<input class="form-check-input" type="radio" name="customer_type" id="typeIndividual" value="individual" checked>
<label class="form-check-label" for="typeIndividual">
<i class="bi bi-person me-1"></i>Bireysel
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="customer_type" id="typeCorporate" value="corporate">
<label class="form-check-label" for="typeCorporate">
<i class="bi bi-building me-1"></i>Kurumsal
</label>
</div>
</div>
</div>
<!-- Bireysel Müşteri Alanları -->
<div id="individualFields">
<div class="row g-3">
<div class="col-md-6">
<label for="name" class="form-label">Ad *</label>
<input type="text" class="form-control" id="name" name="name">
</div>
<div class="col-md-6">
<label for="surname" class="form-label">Soyad *</label>
<input type="text" class="form-control" id="surname" name="surname">
</div>
<div class="col-md-6">
<label for="tc_number" class="form-label">TC Kimlik No</label>
<input type="text" class="form-control" id="tc_number" name="tc_number" maxlength="11" placeholder="11 haneli TC kimlik numarası">
</div>
<div class="col-md-6">
<label for="birth_date" class="form-label">Doğum Tarihi</label>
<input type="date" class="form-control" id="birth_date" name="birth_date">
</div>
</div>
</div>
<!-- Kurumsal Müşteri Alanları -->
<div id="corporateFields" style="display: none;">
<div class="row g-3">
<div class="col-md-6">
<label for="company_name" class="form-label">Şirket Adı *</label>
<input type="text" class="form-control" id="company_name" name="company_name">
</div>
<div class="col-md-6">
<label for="authorized_person" class="form-label">Yetkili Kişi</label>
<input type="text" class="form-control" id="authorized_person" name="authorized_person">
</div>
<div class="col-md-6">
<label for="tax_number" class="form-label">Vergi No</label>
<input type="text" class="form-control" id="tax_number" name="tax_number" placeholder="10 haneli vergi numarası">
</div>
<div class="col-md-6">
<label for="tax_office" class="form-label">Vergi Dairesi</label>
<input type="text" class="form-control" id="tax_office" name="tax_office">
</div>
</div>
</div>
<!-- İletişim Bilgileri -->
<div class="mt-4">
<h6 class="text-muted mb-3">
<i class="bi bi-telephone me-1"></i>İletişim Bilgileri
</h6>
<div class="row g-3">
<div class="col-md-6">
<label for="email" class="form-label">E-posta *</label>
<input type="email" class="form-control" id="email" name="email" required placeholder="ornek@email.com">
</div>
<div class="col-md-6">
<label for="phone" class="form-label">Telefon</label>
<input type="tel" class="form-control" id="phone" name="phone" placeholder="0555 123 45 67">
</div>
<div class="col-12">
<label for="address" class="form-label">Adres</label>
<textarea class="form-control" id="address" name="address" rows="2" placeholder="Tam adres bilgisi..."></textarea>
</div>
</div>
</div>
<!-- Ek Bilgiler -->
<div class="mt-4">
<h6 class="text-muted mb-3">
<i class="bi bi-chat-text me-1"></i>Ek Bilgiler
</h6>
<div class="row g-3">
<div class="col-12">
<label for="notes" class="form-label">Notlar</label>
<textarea class="form-control" id="notes" name="notes" rows="3" placeholder="Müşteri hakkında notlar, özel istekler vb."></textarea>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
<button type="submit" class="btn btn-primary">Kaydet</button>
</div>
</form>
</div>
</div>
</div>
<script>
// Müşteri tipi değiştiğinde alanları göster/gizle
document.querySelectorAll('input[name="customer_type"]').forEach(radio => {
radio.addEventListener('change', function() {
const individualFields = document.getElementById('individualFields');
const corporateFields = document.getElementById('corporateFields');
if (this.value === 'individual') {
individualFields.style.display = 'block';
corporateFields.style.display = 'none';
document.getElementById('name').required = true;
document.getElementById('surname').required = true;
document.getElementById('company_name').required = false;
} else {
individualFields.style.display = 'none';
corporateFields.style.display = 'block';
document.getElementById('name').required = false;
document.getElementById('surname').required = false;
document.getElementById('company_name').required = true;
}
});
});
// Müşteri arama
document.getElementById('customerSearch').addEventListener('keyup', function() {
const searchTerm = this.value.toLowerCase().trim();
const customerItems = document.querySelectorAll('.customer-item');
customerItems.forEach(item => {
const customerName = item.getAttribute('data-customer') || '';
if (searchTerm === '' || customerName.includes(searchTerm)) {
item.style.display = '';
} else {
item.style.display = 'none';
}
});
});
// Form reset
function resetCustomerForm() {
document.getElementById('customerForm').reset();
document.getElementById('customerId').value = '';
document.getElementById('customerModalLabel').textContent = 'Yeni Müşteri Ekle';
document.getElementById('typeIndividual').checked = true;
document.getElementById('individualFields').style.display = 'block';
document.getElementById('corporateFields').style.display = 'none';
document.getElementById('name').required = true;
document.getElementById('surname').required = true;
document.getElementById('company_name').required = false;
}
// Müşteri düzenle
function editCustomer(id) {
fetch(`/get-customer-details/${id}/`)
.then(response => response.json())
.then(data => {
if (data.success) {
const customer = data.customer;
document.getElementById('customerId').value = id;
document.getElementById('customerModalLabel').textContent = 'Müşteri Düzenle';
// Müşteri tipini seç
if (customer.customer_type === 'corporate') {
document.getElementById('typeCorporate').checked = true;
document.getElementById('corporateFields').style.display = 'block';
document.getElementById('individualFields').style.display = 'none';
document.getElementById('name').required = false;
document.getElementById('surname').required = false;
document.getElementById('company_name').required = true;
} else {
document.getElementById('typeIndividual').checked = true;
document.getElementById('individualFields').style.display = 'block';
document.getElementById('corporateFields').style.display = 'none';
document.getElementById('name').required = true;
document.getElementById('surname').required = true;
document.getElementById('company_name').required = false;
}
// Form alanlarını doldur
document.getElementById('name').value = customer.name || '';
document.getElementById('surname').value = customer.surname || '';
document.getElementById('email').value = customer.email || '';
document.getElementById('phone').value = customer.phone || '';
document.getElementById('address').value = customer.address || '';
document.getElementById('notes').value = customer.notes || '';
document.getElementById('tc_number').value = customer.tc_number || '';
document.getElementById('birth_date').value = customer.birth_date || '';
document.getElementById('company_name').value = customer.company_name || '';
document.getElementById('authorized_person').value = customer.authorized_person || '';
document.getElementById('tax_number').value = customer.tax_number || '';
document.getElementById('tax_office').value = customer.tax_office || '';
const modal = new bootstrap.Modal(document.getElementById('customerModal'));
modal.show();
} else {
showToast('❌ Müşteri bilgisi alınamadı!', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showToast('❌ Müşteri bilgisi alınırken hata oluştu!', 'error');
});
}
// Müşteri sil
function deleteCustomer(id) {
if (confirm('Müşteriyi silmek istediğinizden emin misiniz?\nBu işlem müşteriye ait tüm projeleri de etkileyebilir.')) {
fetch(`/musteri/${id}/delete/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast('✅ Müşteri başarıyla silindi', 'success');
setTimeout(() => location.reload(), 1500);
} else {
showToast(`${data.message}`, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showToast('❌ Müşteri silme sırasında hata oluştu!', 'error');
});
}
}
// Form submit
document.getElementById('customerForm').addEventListener('submit', function(e) {
e.preventDefault();
const id = document.getElementById('customerId').value;
const url = id ? `/update-customer/${id}/` : '/musteri/create/';
const formData = new FormData(this);
fetch(url, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken')
},
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast('✅ Müşteri başarıyla kaydedildi', 'success');
const modal = bootstrap.Modal.getInstance(document.getElementById('customerModal'));
if (modal) modal.hide();
setTimeout(() => location.reload(), 1500);
} else {
showToast(`${data.message}`, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showToast('❌ Müşteri kaydedilirken hata oluştu!', 'error');
});
});
// Cookie helper
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
// Toast helper
function showToast(message, type = 'info') {
const toastBody = document.getElementById('mainToastBody');
const toastEl = document.getElementById('mainToast');
toastBody.textContent = message;
// Toast rengini ayarla
toastEl.className = 'toast align-items-center border-0';
if (type === 'success') {
toastEl.classList.add('text-bg-success');
} else if (type === 'error') {
toastEl.classList.add('text-bg-danger');
} else {
toastEl.classList.add('text-bg-info');
}
const toast = new bootstrap.Toast(toastEl);
toast.show();
}
// Sayfa yüklendiğinde URL parametrelerini kontrol et
document.addEventListener('DOMContentLoaded', function() {
const urlParams = new URLSearchParams(window.location.search);
const newCustomerType = urlParams.get('new');
if (newCustomerType && (newCustomerType === 'individual' || newCustomerType === 'corporate')) {
resetCustomerForm();
if (newCustomerType === 'corporate') {
document.getElementById('typeCorporate').checked = true;
document.getElementById('corporateFields').style.display = 'block';
document.getElementById('individualFields').style.display = 'none';
document.getElementById('name').required = false;
document.getElementById('surname').required = false;
document.getElementById('company_name').required = true;
} else {
document.getElementById('typeIndividual').checked = true;
document.getElementById('individualFields').style.display = 'block';
document.getElementById('corporateFields').style.display = 'none';
document.getElementById('name').required = true;
document.getElementById('surname').required = true;
document.getElementById('company_name').required = false;
}
const modal = new bootstrap.Modal(document.getElementById('customerModal'));
modal.show();
// URL'yi temizle
window.history.replaceState({}, document.title, window.location.pathname);
}
});
</script>
{% endblock %}