html file
html file
DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Personal Finance Manager</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/
all.min.css" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary-color: #2c3e50;
--secondary-color: #3498db;
--success-color: #27ae60;
--danger-color: #e74c3c;
--warning-color: #f39c12;
--info-color: #17a2b8;
--light-bg: #ecf0f1;
--dark-bg: #34495e;
--white: #ffffff;
--text-dark: #2c3e50;
--text-light: #7f8c8d;
--shadow: 0 4px 6px rgba(0,0,0,0.1);
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--text-dark);
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
/* Navigation */
.navbar {
background: rgba(44, 62, 80, 0.95);
backdrop-filter: blur(10px);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
box-shadow: var(--shadow);
}
.nav-container {
max-width: 1400px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
}
.logo {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 1.5rem;
font-weight: bold;
color: var(--white);
text-decoration: none;
}
.nav-menu {
display: flex;
list-style: none;
gap: 1rem;
}
.nav-link {
color: var(--white);
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 5px;
transition: all 0.3s ease;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
}
.nav-link:hover,
.nav-link.active {
background: var(--secondary-color);
transform: translateY(-2px);
}
.hamburger {
display: none;
flex-direction: column;
cursor: pointer;
gap: 4px;
}
.hamburger span {
width: 25px;
height: 3px;
background: var(--white);
transition: 0.3s;
}
/* Main Content */
.main-content {
margin-top: 80px;
min-height: calc(100vh - 80px);
padding: 2rem;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.section {
display: none;
}
.section.active {
display: block;
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* Cards */
.card {
background: var(--white);
border-radius: 10px;
box-shadow: var(--shadow);
padding: 1.5rem;
margin-bottom: 1.5rem;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--light-bg);
}
.card-title {
font-size: 1.5rem;
font-weight: bold;
color: var(--primary-color);
display: flex;
align-items: center;
gap: 0.5rem;
}
/* Dashboard Stats */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background: var(--white);
border-radius: 10px;
padding: 1.5rem;
text-align: center;
box-shadow: var(--shadow);
position: relative;
overflow: hidden;
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--success-color), var(--
secondary-color));
}
.stat-icon {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.stat-value {
font-size: 2rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.stat-label {
color: var(--text-light);
font-size: 0.9rem;
}
/* Forms */
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: var(--text-dark);
}
.form-control {
width: 100%;
padding: 0.75rem;
border: 2px solid #ddd;
border-radius: 5px;
font-size: 1rem;
transition: border-color 0.3s ease;
}
.form-control:focus {
outline: none;
border-color: var(--secondary-color);
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
.form-control.error {
border-color: var(--danger-color);
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.checkbox-group,
.radio-group {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.checkbox-item,
.radio-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 5px;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-weight: 600;
}
.btn-primary {
background: var(--secondary-color);
color: var(--white);
}
.btn-success {
background: var(--success-color);
color: var(--white);
}
.btn-danger {
background: var(--danger-color);
color: var(--white);
}
.btn-warning {
background: var(--warning-color);
color: var(--white);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
/* Tables */
.table-container {
overflow-x: auto;
border-radius: 10px;
box-shadow: var(--shadow);
}
.table {
width: 100%;
border-collapse: collapse;
background: var(--white);
}
.table th,
.table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid #eee;
}
.table th {
background: var(--light-bg);
font-weight: 600;
color: var(--primary-color);
}
/* Error Messages */
.error-message {
color: var(--danger-color);
font-size: 0.875rem;
margin-top: 0.25rem;
display: none;
}
.error-message.show {
display: block;
}
/* Success Messages */
.success-message {
background: var(--success-color);
color: var(--white);
padding: 1rem;
border-radius: 5px;
margin-bottom: 1rem;
display: none;
}
.success-message.show {
display: block;
animation: slideDown 0.3s ease;
}
@keyframes slideDown {
from { transform: translateY(-20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
/* Charts Container */
.chart-container {
background: var(--white);
border-radius: 10px;
padding: 1.5rem;
box-shadow: var(--shadow);
margin-bottom: 2rem;
}
.chart-placeholder {
height: 300px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(45deg, #f0f0f0, #e0e0e0);
border-radius: 5px;
color: var(--text-light);
font-size: 1.2rem;
}
/* Loading Spinner */
.loading {
display: none;
text-align: center;
padding: 2rem;
}
.spinner {
border: 3px solid #f3f3f3;
border-top: 3px solid var(--secondary-color);
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 1rem;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Responsive Design */
@media (max-width: 768px) {
.nav-menu {
position: fixed;
top: 80px;
left: -100%;
width: 100%;
height: calc(100vh - 80px);
background: var(--primary-color);
flex-direction: column;
justify-content: flex-start;
align-items: center;
padding-top: 2rem;
transition: left 0.3s ease;
}
.nav-menu.active {
left: 0;
}
.hamburger {
display: flex;
}
.hamburger.active span:nth-child(1) {
transform: rotate(45deg) translate(5px, 5px);
}
.hamburger.active span:nth-child(2) {
opacity: 0;
}
.hamburger.active span:nth-child(3) {
transform: rotate(-45deg) translate(7px, -6px);
}
.main-content {
padding: 1rem;
}
.stats-grid {
grid-template-columns: 1fr;
}
.form-grid {
grid-template-columns: 1fr;
}
.form-row {
grid-template-columns: 1fr;
}
.nav-container {
padding: 1rem;
}
}
@media (max-width: 480px) {
.card {
padding: 1rem;
}
.stat-card {
padding: 1rem;
}
.stat-icon {
font-size: 2rem;
}
.stat-value {
font-size: 1.5rem;
}
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar">
<div class="nav-container">
<a href="#" class="logo">
<i class="fas fa-wallet"></i>
Finance Manager
</a>
<ul class="nav-menu">
<li><a href="#" class="nav-link active" data-section="dashboard">
<i class="fas fa-chart-pie"></i>Dashboard
</a></li>
<li><a href="#" class="nav-link" data-section="transactions">
<i class="fas fa-exchange-alt"></i>Transakcje
</a></li>
<li><a href="#" class="nav-link" data-section="budgets">
<i class="fas fa-calculator"></i>Budżety
</a></li>
<li><a href="#" class="nav-link" data-section="goals">
<i class="fas fa-bullseye"></i>Cele
</a></li>
<li><a href="#" class="nav-link" data-section="reports">
<i class="fas fa-chart-bar"></i>Raporty
</a></li>
<li><a href="#" class="nav-link" data-section="settings">
<i class="fas fa-cog"></i>Ustawienia
</a></li>
</ul>
<div class="hamburger" id="hamburger">
<span></span>
<span></span>
<span></span>
</div>
</div>
</nav>
<div class="card">
<div class="card-header">
<h2 class="card-title">
<i class="fas fa-chart-line"></i>
Przegląd miesięczny
</h2>
<button class="btn btn-primary" id="refresh-data">
<i class="fas fa-sync-alt"></i>Odśwież dane
</button>
</div>
<div class="chart-container">
<div class="chart-placeholder" id="monthly-chart">
<i class="fas fa-chart-area" style="font-size: 3rem;
opacity: 0.3;"></i>
<span style="margin-left: 1rem;">Wykres miesięcznych
wydatków</span>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">
<i class="fas fa-clock"></i>
Ostatnie transakcje
</h2>
<a href="#" class="btn btn-primary" data-
section="transactions">Zobacz wszystkie</a>
</div>
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Data</th>
<th>Opis</th>
<th>Kategoria</th>
<th>Kwota</th>
</tr>
</thead>
<tbody id="recent-transactions">
<tr>
<td colspan="4" style="text-align: center;
color: var(--text-light);">
Brak transakcji do wyświetlenia
</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<div class="form-row">
<div class="form-group">
<label class="form-label" for="goal-
amount">Docelowa kwota *</label>
<input type="number" id="goal-amount"
class="form-control"
placeholder="0.00" step="0.01"
min="0.01" required>
<div class="error-message" id="goal-amount-
error">Wprowadź docelową kwotę</div>
</div>
<div class="form-group">
<label class="form-label" for="goal-
deadline">Termin realizacji</label>
<input type="date" id="goal-deadline"
class="form-control">
</div>
</div>
<div class="form-group">
<label class="form-label" for="goal-
current">Aktualna kwota</label>
<input type="number" id="goal-current" class="form-
control"
placeholder="0.00" step="0.01" min="0">
</div>
<div class="form-group">
<label class="form-label" for="goal-
priority">Priorytet</label>
<div class="radio-group">
<div class="radio-item">
<input type="radio" id="priority-low"
name="goal-priority" value="low">
<label for="priority-low">Niski</label>
</div>
<div class="radio-item">
<input type="radio" id="priority-medium"
name="goal-priority" value="medium" checked>
<label for="priority-medium">Średni</label>
</div>
<div class="radio-item">
<input type="radio" id="priority-high"
name="goal-priority" value="high">
<label for="priority-high">Wysoki</label>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label" for="goal-
description">Opis celu</label>
<textarea id="goal-description" class="form-
control"
placeholder="Opisz swój cel finansowy..."
rows="3"></textarea>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">
<i class="fas fa-bullseye"></i>
Moje cele finansowe
</h2>
</div>
<div id="goals-list">
<p style="text-align: center; color: var(--text-light);
padding: 2rem;">
Brak utworzonych celów finansowych
</p>
</div>
</div>
</div>
</section>
<div class="chart-container">
<h3 style="margin-bottom: 1rem;">Trend wydatków w
czasie</h3>
<div class="chart-placeholder">
<i class="fas fa-chart-line" style="font-size:
3rem; opacity: 0.3;"></i>
<span style="margin-left: 1rem;">Wykres liniowy
trendów</span>
</div>
</div>
</div>
</div>
</section>
<div class="form-group">
<label class="form-label" for="default-
currency">Domyślna waluta</label>
<select id="default-currency" class="form-control">
<option value="PLN">PLN - Polski złoty</option>
<option value="EUR">EUR - Euro</option>
<option value="USD">USD - Dolar
amerykański</option>
<option value="GBP">GBP - Funt
brytyjski</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Powiadomienia</label>
<div class="checkbox-group">
<div class="checkbox-item">
<input type="checkbox" id="email-
notifications" checked>
<label for="email-
notifications">Powiadomienia email</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="budget-
notifications" checked>
<label for="budget-notifications">Alerty
budżetowe</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="goal-
notifications">
<label for="goal-
notifications">Przypomnienia o celach</label>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label" for="data-backup">Backup
danych</label>
<div style="display: flex; gap: 1rem; flex-wrap:
wrap;">
<button type="button" class="btn btn-primary"
id="export-data">
<i class="fas fa-download"></i>Eksportuj
dane
</button>
<button type="button" class="btn btn-warning"
id="import-data">
<i class="fas fa-upload"></i>Importuj dane
</button>
<input type="file" id="import-file"
accept=".json" style="display: none;">
</div>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">
<i class="fas fa-database"></i>
Zarządzanie danymi
</h2>
</div>
<div class="form-group">
<label class="form-label">Statystyki aplikacji</label>
<div class="stats-grid" style="grid-template-columns:
1fr 1fr;">
<div class="stat-card">
<div class="stat-value" id="total-transactions-
count">0</div>
<div class="stat-label">Transakcji</div>
</div>
<div class="stat-card">
<div class="stat-value" id="total-budgets-
count">0</div>
<div class="stat-label">Budżetów</div>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label">Akcje</label>
<div style="display: flex; gap: 1rem; flex-wrap:
wrap;">
<button type="button" class="btn btn-danger"
id="clear-all-data">
<i class="fas fa-trash-alt"></i>Wyczyść
wszystkie dane
</button>
<button type="button" class="btn btn-warning"
id="reset-app">
<i class="fas fa-redo"></i>Resetuj aplikację
</button>
</div>
</div>
</div>
</div>
</section>
</div>
</main>
<script>
// Data storage management
class DataManager {
constructor() {
this.transactions = this.getFromStorage('transactions') || [];
this.budgets = this.getFromStorage('budgets') || [];
this.goals = this.getFromStorage('goals') || [];
this.settings = this.getFromStorage('settings') ||
this.getDefaultSettings();
this.loadSampleData();
}
getFromStorage(key) {
try {
const data = localStorage.getItem(`finance_${key}`);
return data ? JSON.parse(data) : null;
} catch (e) {
console.error('Error loading data from storage:', e);
return null;
}
}
saveToStorage(key, data) {
try {
localStorage.setItem(`finance_${key}`, JSON.stringify(data));
} catch (e) {
console.error('Error saving data to storage:', e);
}
}
getDefaultSettings() {
return {
userName: '',
userEmail: '',
currency: 'PLN',
emailNotifications: true,
budgetNotifications: true,
goalNotifications: false
};
}
loadSampleData() {
// Load sample data asynchronously
if (this.transactions.length === 0) {
this.loadSampleTransactions();
}
}
async loadSampleTransactions() {
// Simulate async data loading
setTimeout(() => {
const sampleTransactions = [
{
id: Date.now() + 1,
type: 'income',
amount: 5000,
date: '2024-01-15',
category: 'salary',
description: 'Wynagrodzenie miesięczne',
recurring: true,
important: true
},
{
id: Date.now() + 2,
type: 'expense',
amount: 150,
date: '2024-01-16',
category: 'food',
description: 'Zakupy spożywcze',
recurring: false,
important: false
},
{
id: Date.now() + 3,
type: 'expense',
amount: 80,
date: '2024-01-17',
category: 'transport',
description: 'Paliwo do samochodu',
recurring: false,
important: false
}
];
this.transactions =
[...this.transactions, ...sampleTransactions];
this.saveTransactions();
app.updateDashboard();
app.renderTransactions();
}, 1000);
}
// Transactions
addTransaction(transaction) {
transaction.id = Date.now();
this.transactions.unshift(transaction);
this.saveTransactions();
}
deleteTransaction(id) {
this.transactions = this.transactions.filter(t => t.id !== id);
this.saveTransactions();
}
saveTransactions() {
this.saveToStorage('transactions', this.transactions);
}
clearTransactions() {
this.transactions = [];
this.saveTransactions();
}
// Budgets
addBudget(budget) {
budget.id = Date.now();
budget.spent = 0;
this.budgets.push(budget);
this.saveBudgets();
}
deleteBudget(id) {
this.budgets = this.budgets.filter(b => b.id !== id);
this.saveBudgets();
}
saveBudgets() {
this.saveToStorage('budgets', this.budgets);
}
// Goals
addGoal(goal) {
goal.id = Date.now();
this.goals.push(goal);
this.saveGoals();
}
deleteGoal(id) {
this.goals = this.goals.filter(g => g.id !== id);
this.saveGoals();
}
saveGoals() {
this.saveToStorage('goals', this.goals);
}
// Settings
saveSettings() {
this.saveToStorage('settings', this.settings);
}
// Statistics
getTotalIncome() {
return this.transactions
.filter(t => t.type === 'income')
.reduce((sum, t) => sum + t.amount, 0);
}
getTotalExpenses() {
return this.transactions
.filter(t => t.type === 'expense')
.reduce((sum, t) => sum + t.amount, 0);
}
getCurrentBalance() {
return this.getTotalIncome() - this.getTotalExpenses();
}
// Data export/import
exportData() {
const data = {
transactions: this.transactions,
budgets: this.budgets,
goals: this.goals,
settings: this.settings,
exportDate: new Date().toISOString()
};
importData(fileContent) {
try {
const data = JSON.parse(fileContent);
if (data.transactions) this.transactions = data.transactions;
if (data.budgets) this.budgets = data.budgets;
if (data.goals) this.goals = data.goals;
if (data.settings) this.settings = data.settings;
this.saveTransactions();
this.saveBudgets();
this.saveGoals();
this.saveSettings();
return true;
} catch (e) {
console.error('Error importing data:', e);
return false;
}
}
clearAllData() {
this.transactions = [];
this.budgets = [];
this.goals = [];
this.settings = this.getDefaultSettings();
localStorage.removeItem('finance_transactions');
localStorage.removeItem('finance_budgets');
localStorage.removeItem('finance_goals');
localStorage.removeItem('finance_settings');
}
}
init() {
this.setupNavigation();
this.setupForms();
this.setupEventListeners();
this.updateDashboard();
this.renderTransactions();
this.renderBudgets();
this.renderGoals();
this.loadSettings();
}
setupNavigation() {
const hamburger = document.getElementById('hamburger');
const navMenu = document.querySelector('.nav-menu');
const navLinks = document.querySelectorAll('.nav-link');
hamburger.addEventListener('click', () => {
hamburger.classList.toggle('active');
navMenu.classList.toggle('active');
});
navLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const section = link.dataset.section;
if (section) {
this.showSection(section);
navLinks.forEach(l => l.classList.remove('active'));
link.classList.add('active');
showSection(sectionName) {
document.querySelectorAll('.section').forEach(section => {
section.classList.remove('active');
});
setupForms() {
// Transaction form
const transactionForm = document.getElementById('transaction-
form');
transactionForm.addEventListener('submit', (e) => {
e.preventDefault();
this.handleTransactionSubmit();
});
// Budget form
const budgetForm = document.getElementById('budget-form');
budgetForm.addEventListener('submit', (e) => {
e.preventDefault();
this.handleBudgetSubmit();
});
// Goal form
const goalForm = document.getElementById('goal-form');
goalForm.addEventListener('submit', (e) => {
e.preventDefault();
this.handleGoalSubmit();
});
// Settings form
const settingsForm = document.getElementById('settings-form');
settingsForm.addEventListener('submit', (e) => {
e.preventDefault();
this.handleSettingsSubmit();
});
setupEventListeners() {
// Dashboard refresh
document.getElementById('refresh-data').addEventListener('click',
() => {
this.updateDashboard();
this.showSuccessMessage('Dane zostały odświeżone!');
});
// Clear transactions
document.getElementById('clear-
transactions').addEventListener('click', () => {
if (confirm('Czy na pewno chcesz usunąć wszystkie
transakcje?')) {
this.dataManager.clearTransactions();
this.updateDashboard();
this.renderTransactions();
this.showSuccessMessage('Wszystkie transakcje zostały
usunięte!');
}
});
// Generate report
document.getElementById('generate-
report').addEventListener('click', () => {
this.generateReport();
});
// Data management
document.getElementById('export-data').addEventListener('click', ()
=> {
this.dataManager.exportData();
this.showSuccessMessage('Dane zostały wyeksportowane!');
});
document.getElementById('import-data').addEventListener('click', ()
=> {
document.getElementById('import-file').click();
});
document.getElementById('import-file').addEventListener('change',
(e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
if (this.dataManager.importData(e.target.result)) {
this.updateDashboard();
this.renderTransactions();
this.renderBudgets();
this.renderGoals();
this.showSuccessMessage('Dane zostały
zaimportowane!');
} else {
alert('Błąd podczas importu danych!');
}
};
reader.readAsText(file);
}
});
document.getElementById('clear-all-data').addEventListener('click',
() => {
if (confirm('Czy na pewno chcesz usunąć wszystkie dane? Ta
operacja jest nieodwracalna!')) {
this.dataManager.clearAllData();
this.updateDashboard();
this.renderTransactions();
this.renderBudgets();
this.renderGoals();
this.showSuccessMessage('Wszystkie dane zostały
usunięte!');
}
});
document.getElementById('reset-app').addEventListener('click', ()
=> {
if (confirm('Czy na pewno chcesz zresetować aplikację?')) {
location.reload();
}
});
}
handleTransactionSubmit() {
const form = document.getElementById('transaction-form');
const formData = new FormData(form);
// Validation
if (!this.validateTransactionForm()) {
return;
}
const transaction = {
type: formData.get('transaction-type'),
amount: parseFloat(document.getElementById('transaction-
amount').value),
date: document.getElementById('transaction-date').value,
category: document.getElementById('transaction-
category').value,
description: document.getElementById('transaction-
description').value,
recurring: document.getElementById('recurring-
transaction').checked,
important: document.getElementById('important-
transaction').checked
};
this.dataManager.addTransaction(transaction);
this.updateDashboard();
this.renderTransactions();
this.showSuccessMessage('Transakcja została dodana!', 'transaction-
success');
form.reset();
validateTransactionForm() {
let isValid = true;
// Validate amount
const amount = document.getElementById('transaction-amount').value;
if (!amount || parseFloat(amount) <= 0) {
this.showError('amount-error', 'transaction-amount');
isValid = false;
}
// Validate date
const date = document.getElementById('transaction-date').value;
if (!date) {
this.showError('date-error', 'transaction-date');
isValid = false;
}
// Validate category
const category = document.getElementById('transaction-
category').value;
if (!category) {
this.showError('category-error', 'transaction-category');
isValid = false;
}
return isValid;
}
handleBudgetSubmit() {
const form = document.getElementById('budget-form');
if (!this.validateBudgetForm()) {
return;
}
const budget = {
name: document.getElementById('budget-name').value,
amount: parseFloat(document.getElementById('budget-
amount').value),
period: document.getElementById('budget-period').value,
category: document.getElementById('budget-category').value,
alert50: document.getElementById('budget-alert-50').checked,
alert80: document.getElementById('budget-alert-80').checked,
alert100: document.getElementById('budget-alert-100').checked
};
this.dataManager.addBudget(budget);
this.renderBudgets();
this.showSuccessMessage('Budżet został utworzony!', 'budget-
success');
form.reset();
}
validateBudgetForm() {
let isValid = true;
const name = document.getElementById('budget-name').value;
if (!name.trim()) {
this.showError('budget-name-error', 'budget-name');
isValid = false;
}
return isValid;
}
handleGoalSubmit() {
const form = document.getElementById('goal-form');
if (!this.validateGoalForm()) {
return;
}
const goal = {
name: document.getElementById('goal-name').value,
targetAmount: parseFloat(document.getElementById('goal-
amount').value),
currentAmount: parseFloat(document.getElementById('goal-
current').value) || 0,
deadline: document.getElementById('goal-deadline').value,
priority: document.querySelector('input[name="goal-
priority"]:checked').value,
description: document.getElementById('goal-description').value
};
this.dataManager.addGoal(goal);
this.renderGoals();
this.showSuccessMessage('Cel finansowy został dodany!', 'goal-
success');
form.reset();
validateGoalForm() {
let isValid = true;
return isValid;
}
handleSettingsSubmit() {
this.dataManager.settings = {
userName: document.getElementById('user-name').value,
userEmail: document.getElementById('user-email').value,
currency: document.getElementById('default-currency').value,
emailNotifications: document.getElementById('email-
notifications').checked,
budgetNotifications: document.getElementById('budget-
notifications').checked,
goalNotifications: document.getElementById('goal-
notifications').checked
};
this.dataManager.saveSettings();
this.showSuccessMessage('Ustawienia zostały zapisane!', 'settings-
success');
}
showError(errorId, inputId) {
const errorElement = document.getElementById(errorId);
const inputElement = document.getElementById(inputId);
if (errorElement) {
errorElement.classList.add('show');
}
if (inputElement) {
inputElement.classList.add('error');
}
}
updateDashboard() {
const totalIncome = this.dataManager.getTotalIncome();
const totalExpenses = this.dataManager.getTotalExpenses();
const currentBalance = this.dataManager.getCurrentBalance();
const totalSavings = Math.max(0, currentBalance * 0.1); // 10% of
balance as savings
document.getElementById('total-income').textContent =
this.formatCurrency(totalIncome);
document.getElementById('total-expenses').textContent =
this.formatCurrency(totalExpenses);
document.getElementById('current-balance').textContent =
this.formatCurrency(currentBalance);
document.getElementById('total-savings').textContent =
this.formatCurrency(totalSavings);
// Update statistics
document.getElementById('total-transactions-count').textContent =
this.dataManager.transactions.length;
document.getElementById('total-budgets-count').textContent =
this.dataManager.budgets.length;
}
renderRecentTransactions() {
const tbody = document.getElementById('recent-transactions');
const recentTransactions = this.dataManager.transactions.slice(0,
5);
if (recentTransactions.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="4" style="text-align: center; color:
var(--text-light);">
Brak transakcji do wyświetlenia
</td>
</tr>
`;
return;
}
<div class="form-row">
<div class="form-group">
<label class="form-label" for="transaction-
amount">Kwota *</label>
<input type="number" id="transaction-amount"
class="form-control"
placeholder="0.00" step="0.01"
min="0.01" required>
<div class="error-message" id="amount-
error">Wprowadź poprawną kwotę</div>
</div>
<div class="form-group">
<label class="form-label" for="transaction-
date">Data *</label>
<input type="date" id="transaction-date"
class="form-control" required>
<div class="error-message" id="date-
error">Wybierz datę transakcji</div>
</div>
</div>
<div class="form-group">
<label class="form-label" for="transaction-
category">Kategoria *</label>
<select id="transaction-category" class="form-
control" required>
<option value="">Wybierz kategorię</option>
<optgroup label="Przychody">
<option
value="salary">Wynagrodzenie</option>
<option value="business">Działalność
gospodarcza</option>
<option
value="investment">Inwestycje</option>
<option value="gift">Prezenty</option>
<option value="other-income">Inne
przychody</option>
</optgroup>
<optgroup label="Wydatki">
<option value="food">Jedzenie</option>
<option
value="transport">Transport</option>
<option value="housing">Mieszkanie</option>
<option
value="entertainment">Rozrywka</option>
<option value="healthcare">Zdrowie</option>
<option value="shopping">Zakupy</option>
<option value="bills">Rachunki</option>
<option value="other-expense">Inne
wydatki</option>
</optgroup>
</select>
<div class="error-message" id="category-
error">Wybierz kategorię</div>
</div>
<div class="form-group">
<label class="form-label" for="transaction-
description">Opis</label>
<textarea id="transaction-description" class="form-
control"
placeholder="Opcjonalny opis
transakcji..." rows="3"></textarea>
</div>
<div class="form-group">
<label class="form-label">Dodatkowe opcje</label>
<div class="checkbox-group">
<div class="checkbox-item">
<input type="checkbox" id="recurring-
transaction">
<label for="recurring-
transaction">Transakcja cykliczna</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="important-
transaction">
<label for="important-transaction">Ważna
transakcja</label>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">
<i class="fas fa-list"></i>
Wszystkie transakcje
</h2>
<button class="btn btn-warning" id="clear-
transactions">
<i class="fas fa-trash"></i>Wyczyść wszystkie
</button>
</div>
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Data</th>
<th>Typ</th>
<th>Opis</th>
<th>Kategoria</th>
<th>Kwota</th>
<th>Akcje</th>
</tr>
</thead>
<tbody id="all-transactions">
<tr>
<td colspan="6" style="text-align: center;
color: var(--text-light);">
Brak transakcji do wyświetlenia
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<div class="form-row">
<div class="form-group">
<label class="form-label" for="budget-
amount">Kwota budżetu *</label>
<input type="number" id="budget-amount"
class="form-control"
placeholder="0.00" step="0.01"
min="0.01" required>
<div class="error-message" id="budget-amount-
error">Wprowadź kwotę budżetu</div>
</div>
<div class="form-group">
<label class="form-label" for="budget-
period">Okres *</label>
<select id="budget-period" class="form-control"
required>
<option value="">Wybierz okres</option>
<option value="weekly">Tygodniowy</option>
<option value="monthly">Miesięczny</option>
<option
value="quarterly">Kwartalny</option>
<option value="yearly">Roczny</option>
</select>
<div class="error-message" id="budget-period-
error">Wybierz okres budżetu</div>
</div>
</div>
<div class="form-group">
<label class="form-label" for="budget-
category">Kategoria budżetu</label>
<select id="budget-category" class="form-control">
<option value="all">Wszystkie
kategorie</option>
<option value="food">Jedzenie</option>
<option value="transport">Transport</option>
<option value="housing">Mieszkanie</option>
<option value="entertainment">Rozrywka</option>
<option value="healthcare">Zdrowie</option>
<option value="shopping">Zakupy</option>
<option value="bills">Rachunki</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Powiadomienia</label>
<div class="checkbox-group">
<div class="checkbox-item">
<input type="checkbox" id="budget-alert-
50">
<label for="budget-alert-50">Powiadom przy
50% budżetu</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="budget-alert-
80">
<label for="budget-alert-80">Powiadom przy
80% budżetu</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="budget-alert-
100">
<label for="budget-alert-100">Powiadom przy
przekroczeniu</label>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">
<i class="fas fa-list"></i>
Aktywne budżety
</h2>
</div>
<div id="budgets-list">
<p style="text-align: center; color: var(--text-light);
padding: 2rem;">
Brak utworzonych budżetów
</p>
</div>
</div>
</div>
</section>