Initial commit: Yakıt Takip Modülü - Akaryakıt İstasyonu Yönetim Sistemi
🚀 Features Implemented: - Full-stack SvelteKit application with Express backend - Role-based authentication (Admin, Fuel Manager, Goods Manager) - Real-time notifications with Socket.IO - SQLite database with auto-initialization in /db directory - Comprehensive user management and fuel slip tracking - Responsive design with Turkish language support 🏗️ Architecture: - Frontend: Svelte + SvelteKit + Vite - Backend: Node.js + Express + Socket.IO - Database: SQLite3 with automatic schema creation - Security: bcrypt password hashing + session management - Real-time: Socket.IO for instant notifications 📁 Project Structure: - Organized documentation in /docs directory - Database files in /db directory with auto-setup - Clean separation of API routes and UI components - Comprehensive documentation including processes, architecture, and user guides 📚 Documentation: - PROJECT_PROCESSES.md: Comprehensive project documentation - KNOWLEDGE_BASE.md: Quick reference and user guide - TEST_GUIDE.md: Testing and quality assurance guide - README_ADMIN_FEATURES.md: Admin functionality guide - Full API documentation and system architecture 🔒 Security Features: - Role-based authorization system - Encrypted password storage with bcrypt - Session-based authentication - SQL injection protection with parameterized queries - CORS configuration and input validation 🎯 Key Features: - Fuel slip creation and approval workflow - Real-time notifications between users - PDF generation for fuel slips - User and vehicle management - Comprehensive audit logging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
928
src/routes/dashboard/units/+page.svelte
Normal file
928
src/routes/dashboard/units/+page.svelte
Normal file
@@ -0,0 +1,928 @@
|
||||
<svelte:head>
|
||||
<style>
|
||||
body {
|
||||
background: #F2F3F7 !important;
|
||||
}
|
||||
</style>
|
||||
</svelte:head>
|
||||
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let user = null;
|
||||
let units = [];
|
||||
let loading = true;
|
||||
let error = '';
|
||||
let showAddModal = false;
|
||||
let showEditModal = false;
|
||||
let selectedUnit = null;
|
||||
|
||||
// Form değişkenleri
|
||||
let formData = {
|
||||
name: '',
|
||||
address: '',
|
||||
stk: '',
|
||||
btk: '',
|
||||
commander: {
|
||||
full_name: '',
|
||||
rank: '',
|
||||
registration_number: '',
|
||||
tc_kimlik: '',
|
||||
phone: ''
|
||||
}
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
const userData = localStorage.getItem('user');
|
||||
if (!userData || JSON.parse(userData).role !== 'admin') {
|
||||
goto('/dashboard');
|
||||
return;
|
||||
}
|
||||
|
||||
user = JSON.parse(userData);
|
||||
await loadUnits();
|
||||
});
|
||||
|
||||
async function loadUnits() {
|
||||
try {
|
||||
const response = await fetch('/api/units');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
units = data.units;
|
||||
} else {
|
||||
error = 'Birlikler yüklenemedi.';
|
||||
}
|
||||
} catch (err) {
|
||||
error = 'Bağlantı hatası.';
|
||||
console.error('Load units error:', err);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
formData = {
|
||||
name: '',
|
||||
address: '',
|
||||
stk: '',
|
||||
btk: '',
|
||||
commander: {
|
||||
full_name: '',
|
||||
rank: '',
|
||||
registration_number: '',
|
||||
tc_kimlik: '',
|
||||
phone: ''
|
||||
}
|
||||
};
|
||||
selectedUnit = null;
|
||||
}
|
||||
|
||||
function openAddModal() {
|
||||
resetForm();
|
||||
showAddModal = true;
|
||||
}
|
||||
|
||||
function openEditModal(unit) {
|
||||
selectedUnit = unit;
|
||||
formData = {
|
||||
name: unit.name,
|
||||
address: unit.address,
|
||||
stk: unit.stk,
|
||||
btk: unit.btk,
|
||||
commander: { ...unit.commander }
|
||||
};
|
||||
showEditModal = true;
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showAddModal = false;
|
||||
showEditModal = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
async function handleAddUnit() {
|
||||
if (!formData.name || !formData.address || !formData.stk || !formData.btk) {
|
||||
error = 'Tüm alanlar zorunludur.';
|
||||
return;
|
||||
}
|
||||
|
||||
const { commander } = formData;
|
||||
if (!commander.full_name || !commander.rank || !commander.registration_number || !commander.tc_kimlik || !commander.phone) {
|
||||
error = 'Birlik sorumlusunun tüm bilgileri zorunludur.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^[0-9]{11}$/.test(commander.tc_kimlik)) {
|
||||
error = 'TC Kimlik numarası 11 haneli olmalıdır.';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/units', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
await loadUnits();
|
||||
closeModal();
|
||||
error = '';
|
||||
} else {
|
||||
error = data.message || 'Birlik eklenemedi.';
|
||||
}
|
||||
} catch (err) {
|
||||
error = 'Bağlantı hatası.';
|
||||
console.error('Add unit error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleUpdateUnit() {
|
||||
if (!formData.name || !formData.address || !formData.stk || !formData.btk) {
|
||||
error = 'Tüm alanlar zorunludur.';
|
||||
return;
|
||||
}
|
||||
|
||||
const { commander } = formData;
|
||||
if (!commander.full_name || !commander.rank || !commander.registration_number || !commander.tc_kimlik || !commander.phone) {
|
||||
error = 'Birlik sorumlusunun tüm bilgileri zorunludur.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^[0-9]{11}$/.test(commander.tc_kimlik)) {
|
||||
error = 'TC Kimlik numarası 11 haneli olmalıdır.';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/units', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: selectedUnit.id,
|
||||
...formData
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
await loadUnits();
|
||||
closeModal();
|
||||
error = '';
|
||||
} else {
|
||||
error = data.message || 'Birlik güncellenemedi.';
|
||||
}
|
||||
} catch (err) {
|
||||
error = 'Bağlantı hatası.';
|
||||
console.error('Update unit error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteUnit(unit) {
|
||||
if (!confirm(`${unit.name} birliğini silmek istediğinizden emin misiniz?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/units', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ id: unit.id }),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
await loadUnits();
|
||||
error = '';
|
||||
} else {
|
||||
error = data.message || 'Birlik silinemedi.';
|
||||
}
|
||||
} catch (err) {
|
||||
error = 'Bağlantı hatası.';
|
||||
console.error('Delete unit error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
goto('/dashboard');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="units-page">
|
||||
<div class="page-header">
|
||||
<div class="header-left">
|
||||
<button class="btn btn-secondary" on:click={goBack}>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M19 12H5"/>
|
||||
<path d="M12 19l-7-7 7-7"/>
|
||||
</svg>
|
||||
Geri
|
||||
</button>
|
||||
<h1 class="page-title">Birlik Yönetimi</h1>
|
||||
</div>
|
||||
<button class="btn btn-primary" on:click={openAddModal}>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="12" y1="5" x2="12" y2="19"/>
|
||||
<line x1="5" y1="12" x2="19" y2="12"/>
|
||||
</svg>
|
||||
Yeni Birlik Ekle
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="error-message">
|
||||
{error}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if loading}
|
||||
<div class="loading-container">
|
||||
<div class="spinner"></div>
|
||||
<p>Yükleniyor...</p>
|
||||
</div>
|
||||
{:else if units.length === 0}
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M3 21h18"/>
|
||||
<path d="M5 21V7l8-4v18"/>
|
||||
<path d="M19 21V11l-6-4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3>Henüz Birlik Yok</h3>
|
||||
<p>Sisteme birlik eklemek için "Yeni Birlik Ekle" butonuna tıklayın.</p>
|
||||
<button class="btn btn-primary" on:click={openAddModal}>
|
||||
İlk Birliği Ekle
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="units-grid">
|
||||
{#each units as unit (unit.id)}
|
||||
<div class="unit-card card">
|
||||
<div class="unit-header">
|
||||
<div class="unit-info">
|
||||
<h3 class="unit-name">{unit.name}</h3>
|
||||
<p class="unit-address">{unit.address}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="unit-details">
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">STK:</span>
|
||||
<span class="detail-value">{unit.stk}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">BTK:</span>
|
||||
<span class="detail-value">{unit.btk}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="commander-section">
|
||||
<h4 class="commander-title">Birlik Sorumlusu</h4>
|
||||
<div class="commander-info">
|
||||
<div class="commander-details">
|
||||
<p class="commander-name">{unit.commander.rank} {unit.commander.full_name}</p>
|
||||
<p class="commander-detail">Sicil: {unit.commander.registration_number}</p>
|
||||
<p class="commander-detail">TC: {unit.commander.tc_kimlik}</p>
|
||||
<p class="commander-detail">İrtibat: {unit.commander.phone}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="unit-actions">
|
||||
<button class="btn btn-sm btn-secondary" on:click={() => openEditModal(unit)}>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
||||
</svg>
|
||||
Düzenle
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger" on:click={() => handleDeleteUnit(unit)}>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
</svg>
|
||||
Sil
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Birlik Ekle Modal -->
|
||||
{#if showAddModal}
|
||||
<div class="modal-overlay" on:click={closeModal}>
|
||||
<div class="modal modal-large" on:click|stopPropagation>
|
||||
<div class="modal-header">
|
||||
<h2>Yeni Birlik Ekle</h2>
|
||||
<button class="modal-close" on:click={closeModal}>×</button>
|
||||
</div>
|
||||
<form on:submit|preventDefault={handleAddUnit} class="modal-form">
|
||||
<div class="form-section">
|
||||
<h3>Birlik Bilgileri</h3>
|
||||
<div class="form-group">
|
||||
<label for="name">Birlik Adı</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.name}
|
||||
placeholder="1. Motorlu Piyade Tugayı"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="address">Adres</label>
|
||||
<input
|
||||
id="address"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.address}
|
||||
placeholder="Mecidiyeköy, Şişli/İstanbul"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="stk">STK</label>
|
||||
<input
|
||||
id="stk"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.stk}
|
||||
placeholder="STK-12345"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="btk">BTK</label>
|
||||
<input
|
||||
id="btk"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.btk}
|
||||
placeholder="BTK-67890"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Birlik Sorumlusu</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="commander-name">Adı Soyadı</label>
|
||||
<input
|
||||
id="commander-name"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.commander.full_name}
|
||||
placeholder="Mehmet Yılmaz"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="commander-rank">Rütbesi</label>
|
||||
<input
|
||||
id="commander-rank"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.commander.rank}
|
||||
placeholder="Yüzbaşı"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="commander-registration">Sicil No</label>
|
||||
<input
|
||||
id="commander-registration"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.commander.registration_number}
|
||||
placeholder="123456"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="commander-phone">İrtibat No</label>
|
||||
<input
|
||||
id="commander-phone"
|
||||
type="tel"
|
||||
class="form-input"
|
||||
bind:value={formData.commander.phone}
|
||||
placeholder="05321234567"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="commander-tc">TC Kimlik Numarası</label>
|
||||
<input
|
||||
id="commander-tc"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.commander.tc_kimlik}
|
||||
placeholder="12345678901"
|
||||
maxlength="11"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn btn-secondary" on:click={closeModal}>İptal</button>
|
||||
<button type="submit" class="btn btn-primary">Kaydet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Birlik Düzenle Modal -->
|
||||
{#if showEditModal}
|
||||
<div class="modal-overlay" on:click={closeModal}>
|
||||
<div class="modal modal-large" on:click|stopPropagation>
|
||||
<div class="modal-header">
|
||||
<h2>Birlik Düzenle</h2>
|
||||
<button class="modal-close" on:click={closeModal}>×</button>
|
||||
</div>
|
||||
<form on:submit|preventDefault={handleUpdateUnit} class="modal-form">
|
||||
<div class="form-section">
|
||||
<h3>Birlik Bilgileri</h3>
|
||||
<div class="form-group">
|
||||
<label for="edit-name">Birlik Adı</label>
|
||||
<input
|
||||
id="edit-name"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.name}
|
||||
placeholder="1. Motorlu Piyade Tugayı"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit-address">Adres</label>
|
||||
<input
|
||||
id="edit-address"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.address}
|
||||
placeholder="Mecidiyeköy, Şişli/İstanbul"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="edit-stk">STK</label>
|
||||
<input
|
||||
id="edit-stk"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.stk}
|
||||
placeholder="STK-12345"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit-btk">BTK</label>
|
||||
<input
|
||||
id="edit-btk"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.btk}
|
||||
placeholder="BTK-67890"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Birlik Sorumlusu</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="edit-commander-name">Adı Soyadı</label>
|
||||
<input
|
||||
id="edit-commander-name"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.commander.full_name}
|
||||
placeholder="Mehmet Yılmaz"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit-commander-rank">Rütbesi</label>
|
||||
<input
|
||||
id="edit-commander-rank"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.commander.rank}
|
||||
placeholder="Yüzbaşı"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="edit-commander-registration">Sicil No</label>
|
||||
<input
|
||||
id="edit-commander-registration"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.commander.registration_number}
|
||||
placeholder="123456"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit-commander-phone">İrtibat No</label>
|
||||
<input
|
||||
id="edit-commander-phone"
|
||||
type="tel"
|
||||
class="form-input"
|
||||
bind:value={formData.commander.phone}
|
||||
placeholder="05321234567"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit-commander-tc">TC Kimlik Numarası</label>
|
||||
<input
|
||||
id="edit-commander-tc"
|
||||
type="text"
|
||||
class="form-input"
|
||||
bind:value={formData.commander.tc_kimlik}
|
||||
placeholder="12345678901"
|
||||
maxlength="11"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn btn-secondary" on:click={closeModal}>İptal</button>
|
||||
<button type="submit" class="btn btn-primary">Güncelle</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.units-page {
|
||||
padding: 2rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-color);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #FEE2E2;
|
||||
color: #DC2626;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1.5rem;
|
||||
border: 1px solid #FECACA;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3rem;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #E5E7EB;
|
||||
border-top: 4px solid var(--primary-color);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--card-border-color);
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.empty-state h3 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.units-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.unit-card {
|
||||
background: white;
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.unit-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.unit-header {
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--card-border-color);
|
||||
}
|
||||
|
||||
.unit-name {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.unit-address {
|
||||
color: var(--text-secondary);
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.unit-details {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.commander-section {
|
||||
background: #F9FAFB;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.commander-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin: 0 0 0.75rem 0;
|
||||
}
|
||||
|
||||
.commander-name {
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.commander-detail {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.unit-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #DC2626;
|
||||
color: white;
|
||||
border: 1px solid #B91C1C;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #B91C1C;
|
||||
}
|
||||
|
||||
/* Modal Stilleri */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-large {
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid var(--card-border-color);
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
margin: 0;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.modal-form {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-section h3 {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin: 0 0 1rem 0;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--card-border-color);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.form-row .form-group {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: flex-end;
|
||||
margin-top: 2rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 1px solid var(--card-border-color);
|
||||
}
|
||||
|
||||
/* Responsive Tasarım */
|
||||
@media (max-width: 768px) {
|
||||
.units-page {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.units-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.unit-actions {
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.modal {
|
||||
margin: 0;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.modal-large {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user