# Frontend Mimarisi Dokümantasyonu Yakıt Takip Sistemi frontend mimarisi, Svelte bileşenleri, state management ve kullanıcı etkileşimi hakkında detaylı bilgi. ## 📋 İçerlik - [Mimari Genel Bakış](#mimari-genel-bakış) - [Component Hiyerarşisi](#component-hiyerarşisi) - [State Management](#state-management) - [Routing](#routing) - [API Entegrasyonu](#api-entegrasyonu) - [Styling ve Tasarım](#styling-ve-tasarım) - [Performance Optimizasyonu](#performance-optimizasyonu) ## 🏗️ Mimari Genel Bakış ### Teknoloji Stack - **Framework**: Svelte 4.x - **Build Tool**: Vite 5.x - **Bundler**: Rollup (via Vite) - **CSS**: Pure CSS3 (no framework) - **Icons**: Font Awesome 6.x - **Real-time**: Socket.IO Client - **HTTP Client**: Fetch API (custom wrapper) ### Tasarım Prensipleri - **Component-based**: Tek sorumluluk ilkesi - **Reactive**: Svelte reaktif değişkenleri - **Event-driven**: Custom events ile haberleşme - **Mobile-first**: Responsive tasarım - **Accessibility**: WCAG 2.1 AA uyumluluğu ### Proje Yapısı ``` client/src/ ├── components/ # Svelte bileşenleri │ ├── LoginView.svelte # Giriş ekranı │ ├── AdminPanel.svelte # Admin yönetim paneli │ ├── FuelManagerPanel.svelte # Yakıt sorumlusu paneli │ ├── InventoryManagerPanel.svelte # Mal sorumlusu paneli │ └── RoleWelcome.svelte # Rol karşılama ekranı ├── lib/ # Yardımcı kütüphaneler │ └── numberToWordsTr.js # Sayı→metin çeviri ├── api.js # API istemcisi ├── app.css # Global stiller ├── main.js # Uygulama başlangıcı └── App.svelte # Ana bileşen ``` ## 🧩 Component Hiyerarşisi ### App.svelte (Root Component) **Amaç**: Uygulamanın ana bileşeni ve routing mantığı #### Sorumluluklar - Oturum yönetimi (localStorage + state) - Component routing (role-based) - Global durum yönetimi - Layout ve tema yönetimi - Error boundaries #### Key Features ```javascript // State management let user = null; let token = null; let loading = false; let feedback = ''; // Session persistence onMount(async () => { const storedToken = localStorage.getItem('sessionToken'); if (storedToken) { token = storedToken; // Validate session } }); // Event handling function handleLoginSuccess(event) { user = event.detail.user; token = event.detail.token; localStorage.setItem('sessionToken', token); } ``` #### Template Structure ```html
{#if user} {#if user.role === 'admin'} {:else if user.role === 'fuel_manager'} {:else if user.role === 'inventory_manager'} {:else} {/if} {:else} {/if}
``` ### LoginView.svelte **Amaç**: Kullanıcı kimlik doğrulama arayüzü #### Features - Form validasyonu - Hata yönetimi - Loading states - Remember me (localStorage) - Custom event dispatch #### State Management ```javascript let username = ''; let password = ''; let loading = false; let error = ''; // Form validasyonu function validateForm() { if (!username.trim() || !password.trim()) { error = 'Kullanıcı adı ve şifre zorunludur.'; return false; } return true; } // API çağrısı async function handleLogin() { if (!validateForm()) return; loading = true; error = ''; try { const response = await login(username, password); dispatch('success', { user: response.user, token: response.token }); } catch (err) { error = err.message || 'Giriş başarısız.'; } finally { loading = false; } } ``` ### AdminPanel.svelte **Amaç**: Admin yönetim paneli #### Modules - Kullanıcı yönetimi (Mal sorumluları) - Araç yönetimi - Birlik yönetimi - Personel yönetimi #### Component Pattern ```javascript // Tab module state let activeTab = 'managers'; let managers = []; let vehicles = []; let units = []; let personnel = []; let showCreateModal = false; let editingItem = null; let loading = false; // Data fetching async function loadManagers() { loading = true; try { const response = await listInventoryManagers(token); managers = response.managers; } catch (err) { console.error('Managers load error:', err); } finally { loading = false; } } // CRUD operations async function handleCreateManager(data) { try { await createInventoryManager(data, token); await loadManagers(); showCreateModal = false; } catch (err) { error = err.message; } } ``` ### FuelManagerPanel.svelte **Amaç**: Yakıt sorumlusu işlemleri paneli #### Key Features - Kaynak yönetimi (araç, birlik, personel) - Yakıt fişi oluşturma - Fiş takibi ve yönetimi - PDF indirme - Real-time bildirimler #### Socket.IO Integration ```javascript import { io } from 'socket.io-client'; let socket = null; onMount(() => { socket = io('http://localhost:5005', { auth: { token } }); socket.on('fuelSlip:status', (slip) => { // Update UI with new status const index = slips.findIndex(s => s.id === slip.id); if (index !== -1) { slips[index] = slip; } }); return () => { if (socket) socket.disconnect(); }; }); ``` ### InventoryManagerPanel.svelte **Amaç**: Mal sorumlusu onay paneli #### Features - Atanan fiş listesi - Onay/reddetme işlemleri - Real-time bildirimler - Filtreleme ve arama #### Status Management ```javascript let assignedSlips = []; let filter = 'all'; // all, pending, approved, rejected function getFilteredSlips() { if (filter === 'all') return assignedSlips; return assignedSlips.filter(slip => slip.status === filter); } async function handleStatusUpdate(slipId, status, reason = null) { try { const response = await updateFuelSlipStatus(slipId, status, reason, token); // Update local state const index = assignedSlips.findIndex(s => s.id === slipId); if (index !== -1) { assignedSlips[index] = response.slip; } } catch (err) { error = err.message; } } ``` ## 📊 State Management ### Global State (App.svelte) ```javascript // User session state let user = null; let token = null; let loading = false; // Session persistence function saveSession(userData, sessionToken) { user = userData; token = sessionToken; localStorage.setItem('sessionToken', sessionToken); } function clearSession() { user = null; token = null; localStorage.removeItem('sessionToken'); } ``` ### Component State Patterns #### Form State ```javascript // Generic form state pattern let formData = { username: '', password: '', remember: false }; let errors = {}; let touched = {}; let isSubmitting = false; function validateField(field) { touched[field] = true; // Validation logic } function isValidForm() { return Object.keys(errors).every(key => !errors[key]); } ``` #### List State ```javascript // Generic list state pattern let items = []; let loading = false; let error = null; let selectedItems = []; async function loadItems() { loading = true; error = null; try { const response = await fetchItems(token); items = response.data; } catch (err) { error = err.message; } finally { loading = false; } } function handleItemSelect(itemId) { const index = selectedItems.indexOf(itemId); if (index > -1) { selectedItems.splice(index, 1); } else { selectedItems.push(itemId); } } ``` ### Reactive State with Svelte ```javascript // Reactive statements $: filteredItems = items.filter(item => item.name.toLowerCase().includes(searchTerm.toLowerCase()) ); $: totalAmount = items.reduce((sum, item) => sum + item.amount, 0); $: isValid = formData.username && formData.password && !Object.values(errors).some(Boolean); ``` ## 🛣️ Routing ### Role-based Routing ```javascript // App.svelte routing logic {#if user.role === 'admin'} {:else if user.role === 'fuel_manager'} {:else if user.role === 'inventory_manager'} {:else} {/if} ``` ### Tab-based Navigation ```javascript // Tab management pattern let activeTab = 'overview'; const tabs = [ { id: 'overview', label: 'Genel Bakış', icon: 'dashboard' }, { id: 'managers', label: 'Mal Sorumluları', icon: 'users' }, { id: 'vehicles', label: 'Araçlar', icon: 'car' }, { id: 'units', label: 'Birlikler', icon: 'building' }, { id: 'personnel', label: 'Personel', icon: 'user-tie' } ]; function switchTab(tabId) { activeTab = tabId; loadTabData(tabId); } ``` ## 🌐 API Entegrasyonu ### API Client (api.js) ```javascript // Base configuration const BASE_URL = 'http://localhost:5005/api'; class ApiClient { constructor() { this.token = null; } setToken(token) { this.token = token; } async request(endpoint, options = {}) { const url = `${BASE_URL}${endpoint}`; const config = { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options }; if (this.token) { config.headers['X-Session-Token'] = this.token; } const response = await fetch(url, config); if (!response.ok) { const error = await response.json(); throw new Error(error.message || 'API request failed'); } return response.json(); } // Authentication methods async login(username, password) { return this.request('/auth/login', { method: 'POST', body: JSON.stringify({ username, password }) }); } async logout() { return this.request('/auth/logout', { method: 'POST' }); } // CRUD methods async get(endpoint) { return this.request(endpoint); } async post(endpoint, data) { return this.request(endpoint, { method: 'POST', body: JSON.stringify(data) }); } async put(endpoint, data) { return this.request(endpoint, { method: 'PUT', body: JSON.stringify(data) }); } async delete(endpoint) { return this.request(endpoint, { method: 'DELETE' }); } } // Singleton instance const api = new ApiClient(); export default api; ``` ### Usage in Components ```javascript import api from './api.js'; // Component usage let token = ''; async function loadData() { try { const data = await api.get('/vehicles'); vehicles = data.vehicles; } catch (error) { console.error('Load error:', error); errorMessage = error.message; } } async function handleSubmit() { try { isSubmitting = true; const result = await api.post('/vehicles', formData); await loadData(); showSuccess('Araç başarıyla eklendi'); } catch (error) { showError(error.message); } finally { isSubmitting = false; } } ``` ## 🎨 Styling ve Tasarım ### CSS Architecture ```css /* Global variables (app.css) */ :root { /* Colors */ --primary-color: #4c6ef5; --secondary-color: #6c757d; --success-color: #51cf66; --warning-color: #ffd43b; --danger-color: #ff6b6b; /* Spacing */ --spacing-xs: 0.25rem; --spacing-sm: 0.5rem; --spacing-md: 1rem; --spacing-lg: 1.5rem; --spacing-xl: 2rem; /* Typography */ --font-size-xs: 0.75rem; --font-size-sm: 0.875rem; --font-size-base: 1rem; --font-size-lg: 1.125rem; --font-size-xl: 1.25rem; /* Shadows */ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07); --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); } ``` ### Component Styling Pattern ```css /* Component-specific styles */ .panel-container { display: flex; flex-direction: column; gap: var(--spacing-lg); padding: var(--spacing-xl); } .tab-navigation { display: flex; border-bottom: 2px solid var(--border-color); margin-bottom: var(--spacing-lg); } .tab-button { padding: var(--spacing-sm) var(--spacing-lg); border: none; background: none; color: var(--text-muted); cursor: pointer; transition: all 0.2s ease; border-bottom: 2px solid transparent; } .tab-button:hover { color: var(--primary-color); background: var(--primary-light); } .tab-button.active { color: var(--primary-color); border-bottom-color: var(--primary-color); } ``` ### Responsive Design ```css /* Mobile-first responsive design */ .content { padding: var(--spacing-md); } @media (min-width: 768px) { .content { padding: var(--spacing-lg); } } @media (min-width: 1024px) { .content { padding: var(--spacing-xl); max-width: 1280px; margin: 0 auto; } } .grid { display: grid; gap: var(--spacing-md); } @media (min-width: 768px) { .grid { grid-template-columns: repeat(2, 1fr); } } @media (min-width: 1024px) { .grid { grid-template-columns: repeat(3, 1fr); } } ``` ## ⚡ Performance Optimizasyonu ### Code Splitting ```javascript // Dynamic imports for lazy loading const AdminPanel = lazy(() => import('./components/AdminPanel.svelte')); const FuelManagerPanel = lazy(() => import('./components/FuelManagerPanel.svelte')); // Usage with suspense {#if user.role === 'admin'} Loading...}> {/if} ``` ### Component Optimization ```javascript // Reactivity optimization $: expensiveValue = computeExpensiveValue(data); // Memoization with Svelte function memoize(fn) { let lastArgs; let lastResult; return (...args) => { if (args.length !== lastArgs?.length || args.some((arg, i) => arg !== lastArgs[i])) { lastArgs = args; lastResult = fn(...args); } return lastResult; }; } const memoizedFilter = memoize((items, filter) => items.filter(item => matchesFilter(item, filter)) ); ``` ### Asset Optimization ```javascript // vite.config.js export default { build: { rollupOptions: { output: { manualChunks: { vendor: ['svelte'], socketio: ['socket.io-client'], utils: ['./src/lib/numberToWordsTr.js'] } } }, assetsInlineLimit: 4096 }, optimizeDeps: { include: ['svelte', 'socket.io-client'] } }; ``` ### Bundle Analysis ```bash # Bundle analyzer npm install --save-dev rollup-plugin-visualizer # Build with analysis npm run build -- --analyze ``` ## 🔧 Development Tools ### Svelte DevTools ```javascript // DevTools integration if (import.meta.env.DEV) { import('svelte-devtools'); } ``` ### Hot Module Replacement ```javascript // Vite HMR configuration export default { server: { hmr: { overlay: true } } }; ``` ### TypeScript Support (Optional) ```javascript // svelte.config.js import { vitePreprocess } from '@sveltejs/kit/vite'; export default { preprocess: vitePreprocess(), compilerOptions: { dev: import.meta.env.DEV } }; ``` --- **Not**: Frontend mimarisi sürekli olarak geliştirilmektedir. Yeni özellikler eklerken bu dokümantasyonu güncel tutun.