Readme index'ler eklendi.

This commit is contained in:
2025-11-03 23:12:45 +03:00
parent 7edbab2689
commit 63abf6eaff
10 changed files with 3867 additions and 0 deletions

731
docs/FRONTEND.md Normal file
View File

@@ -0,0 +1,731 @@
# 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
<div class="app-shell">
<header class="hero">
<!-- Hero content with user info -->
</header>
<main class="content">
{#if user}
<!-- Role-based component rendering -->
{#if user.role === 'admin'}
<AdminPanel {token} />
{:else if user.role === 'fuel_manager'}
<FuelManagerPanel {user} {token} />
{:else if user.role === 'inventory_manager'}
<InventoryManagerPanel {user} {token} />
{:else}
<RoleWelcome {user} />
{/if}
{:else}
<LoginView on:success={handleLoginSuccess} />
{/if}
</main>
</div>
```
### 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'}
<AdminPanel {token} />
{:else if user.role === 'fuel_manager'}
<FuelManagerPanel {user} {token} />
{:else if user.role === 'inventory_manager'}
<InventoryManagerPanel {user} {token} />
{:else}
<RoleWelcome {user} />
{/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'}
<Suspense fallback={<div>Loading...</div>}>
<AdminPanel {token} />
</Suspense>
{/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.