fina/app/templates/income.html

321 lines
24 KiB
HTML
Raw Normal View History

2025-12-26 00:52:56 +00:00
{% extends "base.html" %}
{% block title %}Income - FINA{% endblock %}
{% block body %}
<div class="flex h-screen w-full">
<!-- Side Navigation -->
<aside id="sidebar" class="hidden lg:flex w-64 flex-col bg-sidebar-light dark:bg-background-dark border-r border-border-light dark:border-[#233648] transition-all duration-300 shadow-sm dark:shadow-none">
<div class="p-6 flex flex-col h-full justify-between">
<div class="flex flex-col gap-8">
<!-- User Profile -->
<div class="flex gap-3 items-center">
<img src="{{ current_user.avatar | avatar_url }}" alt="{{ current_user.username }}" class="size-10 rounded-full border-2 border-primary/30 object-cover">
<div class="flex flex-col">
<h1 class="text-text-main dark:text-white text-base font-bold leading-none">{{ current_user.username }}</h1>
<p class="text-text-muted dark:text-[#92adc9] text-xs font-normal mt-1">
{% if current_user.is_admin %}<span data-translate="user.admin">Admin</span>{% else %}<span data-translate="user.user">User</span>{% endif %}
</p>
</div>
</div>
<!-- Navigation Links -->
<nav class="flex flex-col gap-2">
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.dashboard') }}">
<span class="material-symbols-outlined text-[20px]">dashboard</span>
<span class="text-sm font-medium" data-translate="nav.dashboard">Dashboard</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.transactions') }}">
<span class="material-symbols-outlined text-[20px]">receipt_long</span>
<span class="text-sm font-medium" data-translate="nav.transactions">Transactions</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg bg-primary/10 text-primary border border-primary/10" href="{{ url_for('main.income') }}">
<span class="material-symbols-outlined text-[20px]">payments</span>
<span class="text-sm font-medium" data-translate="nav.income">Income</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.recurring') }}">
<span class="material-symbols-outlined text-[20px]">repeat</span>
<span class="text-sm font-medium" data-translate="nav.recurring">Recurring</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.import_page') }}">
<span class="material-symbols-outlined text-[20px]">file_upload</span>
<span class="text-sm font-medium" data-translate="nav.import">Import CSV</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.reports') }}">
<span class="material-symbols-outlined text-[20px]">pie_chart</span>
<span class="text-sm font-medium" data-translate="nav.reports">Reports</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.documents') }}">
<span class="material-symbols-outlined text-[20px]">folder_open</span>
<span class="text-sm font-medium" data-translate="nav.documents">Documents</span>
</a>
{% if current_user.is_admin %}
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="/admin">
<span class="material-symbols-outlined text-[20px]">admin_panel_settings</span>
<span class="text-sm font-medium" data-translate="nav.admin">Admin</span>
</a>
{% endif %}
</nav>
</div>
<!-- Bottom Links -->
<div class="flex flex-col gap-2">
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.settings') }}">
<span class="material-symbols-outlined text-[20px]">settings</span>
<span class="text-sm font-medium" data-translate="nav.settings">Settings</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-red-500 dark:hover:text-red-400 transition-colors" href="{{ url_for('auth.logout') }}">
<span class="material-symbols-outlined text-[20px]">logout</span>
<span class="text-sm font-medium" data-translate="nav.logout">Logout</span>
</a>
</div>
</div>
</aside>
<!-- Main Content Area -->
<div class="flex-1 flex flex-col min-h-screen overflow-hidden">
<!-- Top Bar (Mobile) -->
<header class="lg:hidden bg-white dark:bg-card-dark border-b border-border-light dark:border-[#233648] p-4 flex items-center justify-between">
<button id="mobile-menu-toggle" class="text-text-main dark:text-white p-2 hover:bg-slate-100 dark:hover:bg-[#233648] rounded-lg transition-colors">
<span class="material-symbols-outlined text-[24px]">menu</span>
</button>
<h1 class="text-lg font-bold text-text-main dark:text-white" data-translate="income.title">Income</h1>
<div class="w-10"></div>
</header>
<!-- Content -->
<main class="flex-1 overflow-y-auto bg-background-light dark:bg-background-dark p-4 md:p-6 lg:p-8">
<div class="max-w-7xl mx-auto">
<!-- Header -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-6 gap-4">
<div>
<h1 class="text-2xl md:text-3xl font-bold text-text-main dark:text-white mb-2" data-translate="income.title">Income</h1>
<p class="text-text-muted dark:text-[#92adc9]" data-translate="income.subtitle">Track your income sources</p>
</div>
<button onclick="openIncomeModal()" class="inline-flex items-center gap-2 bg-primary hover:bg-primary/90 text-white px-6 py-3 rounded-xl font-medium transition-all hover:shadow-lg">
<span class="material-symbols-outlined text-[20px]">add</span>
<span data-translate="income.addNew">Add Income</span>
</button>
</div>
<!-- Income Table -->
<div class="bg-white dark:bg-card-dark rounded-2xl border border-border-light dark:border-[#233648] overflow-hidden shadow-sm">
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-slate-50 dark:bg-[#111a22] border-b border-border-light dark:border-[#233648]">
<tr>
<th class="px-6 py-4 text-left text-xs font-medium text-text-muted dark:text-[#92adc9] uppercase tracking-wider" data-translate="income.tableDescription">Description</th>
<th class="px-6 py-4 text-left text-xs font-medium text-text-muted dark:text-[#92adc9] uppercase tracking-wider" data-translate="income.tableDate">Date</th>
<th class="px-6 py-4 text-left text-xs font-medium text-text-muted dark:text-[#92adc9] uppercase tracking-wider" data-translate="income.tableSource">Source</th>
<th class="px-6 py-4 text-right text-xs font-medium text-text-muted dark:text-[#92adc9] uppercase tracking-wider" data-translate="income.tableAmount">Amount</th>
<th class="px-6 py-4 text-right text-xs font-medium text-text-muted dark:text-[#92adc9] uppercase tracking-wider" data-translate="income.tableActions">Actions</th>
</tr>
</thead>
<tbody id="income-table-body">
<!-- Income entries will be populated by JavaScript -->
</tbody>
</table>
</div>
</div>
</div>
</main>
</div>
</div>
<!-- Mobile Menu Overlay -->
<div id="mobile-menu" class="lg:hidden hidden fixed inset-0 bg-black/60 backdrop-blur-sm z-50">
<aside class="w-64 h-full bg-sidebar-light dark:bg-card-dark border-r border-border-light dark:border-[#233648] overflow-y-auto">
<div class="p-6 flex flex-col h-full justify-between">
<div class="flex flex-col gap-8">
<!-- Close Button -->
<button id="mobile-menu-close" class="self-end text-text-main dark:text-white p-2 hover:bg-slate-100 dark:hover:bg-[#233648] rounded-lg transition-colors">
<span class="material-symbols-outlined text-[24px]">close</span>
</button>
<!-- User Profile -->
<div class="flex gap-3 items-center">
<img src="{{ current_user.avatar | avatar_url }}" alt="{{ current_user.username }}" class="size-10 rounded-full border-2 border-primary/30 object-cover">
<div class="flex flex-col">
<h1 class="text-text-main dark:text-white text-base font-bold leading-none">{{ current_user.username }}</h1>
<p class="text-text-muted dark:text-[#92adc9] text-xs font-normal mt-1">
{% if current_user.is_admin %}<span data-translate="user.admin">Admin</span>{% else %}<span data-translate="user.user">User</span>{% endif %}
</p>
</div>
</div>
<!-- Navigation Links -->
<nav class="flex flex-col gap-2">
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.dashboard') }}">
<span class="material-symbols-outlined text-[20px]">dashboard</span>
<span class="text-sm font-medium" data-translate="nav.dashboard">Dashboard</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.transactions') }}">
<span class="material-symbols-outlined text-[20px]">receipt_long</span>
<span class="text-sm font-medium" data-translate="nav.transactions">Transactions</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg bg-primary/10 text-primary border border-primary/10" href="{{ url_for('main.income') }}">
<span class="material-symbols-outlined text-[20px]">payments</span>
<span class="text-sm font-medium" data-translate="nav.income">Income</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.recurring') }}">
<span class="material-symbols-outlined text-[20px]">repeat</span>
<span class="text-sm font-medium" data-translate="nav.recurring">Recurring</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.import_page') }}">
<span class="material-symbols-outlined text-[20px]">file_upload</span>
<span class="text-sm font-medium" data-translate="nav.import">Import CSV</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.reports') }}">
<span class="material-symbols-outlined text-[20px]">pie_chart</span>
<span class="text-sm font-medium" data-translate="nav.reports">Reports</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.documents') }}">
<span class="material-symbols-outlined text-[20px]">folder_open</span>
<span class="text-sm font-medium" data-translate="nav.documents">Documents</span>
</a>
{% if current_user.is_admin %}
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="/admin">
<span class="material-symbols-outlined text-[20px]">admin_panel_settings</span>
<span class="text-sm font-medium" data-translate="nav.admin">Admin</span>
</a>
{% endif %}
</nav>
</div>
<!-- Bottom Links -->
<div class="flex flex-col gap-2">
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-text-main dark:hover:text-white transition-colors" href="{{ url_for('main.settings') }}">
<span class="material-symbols-outlined text-[20px]">settings</span>
<span class="text-sm font-medium" data-translate="nav.settings">Settings</span>
</a>
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-muted dark:text-[#92adc9] hover:bg-slate-50 dark:hover:bg-[#233648] hover:text-red-500 dark:hover:text-red-400 transition-colors" href="{{ url_for('auth.logout') }}">
<span class="material-symbols-outlined text-[20px]">logout</span>
<span class="text-sm font-medium" data-translate="nav.logout">Logout</span>
</a>
</div>
</div>
</aside>
</div>
<!-- Income Modal -->
<div id="income-modal" class="hidden fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4" onclick="if (event.target === this) closeIncomeModal()">
<div class="bg-white dark:bg-card-dark rounded-2xl max-w-lg w-full border border-border-light dark:border-[#233648] shadow-2xl">
<div class="p-6 border-b border-border-light dark:border-[#233648]">
<h3 id="income-modal-title" class="text-xl font-bold text-text-main dark:text-white" data-translate="income.add">Add Income</h3>
</div>
<form id="income-form" class="p-6">
<div class="space-y-4">
<!-- Amount -->
<div>
<label for="income-amount" class="block text-sm font-medium text-text-main dark:text-white mb-2" data-translate="form.amount">Amount</label>
<input type="number" id="income-amount" step="0.01" min="0" required
class="w-full bg-slate-50 dark:bg-background-dark border border-border-light dark:border-[#233648] rounded-lg px-4 py-3 text-text-main dark:text-white focus:outline-none focus:ring-2 focus:ring-primary">
</div>
<!-- Source -->
<div>
<label for="income-source" class="block text-sm font-medium text-text-main dark:text-white mb-2" data-translate="income.source">Source</label>
<select id="income-source" required class="income-source-select w-full bg-slate-50 dark:bg-background-dark border border-border-light dark:border-[#233648] rounded-lg px-4 py-3 text-text-main dark:text-white focus:outline-none focus:ring-2 focus:ring-primary">
<option value="">Select source...</option>
</select>
</div>
<!-- Description -->
<div>
<label for="income-description" class="block text-sm font-medium text-text-main dark:text-white mb-2" data-translate="form.description">Description</label>
<input type="text" id="income-description" required
class="w-full bg-slate-50 dark:bg-background-dark border border-border-light dark:border-[#233648] rounded-lg px-4 py-3 text-text-main dark:text-white focus:outline-none focus:ring-2 focus:ring-primary">
</div>
<!-- Date -->
<div>
<label for="income-date" class="block text-sm font-medium text-text-main dark:text-white mb-2" data-translate="form.date">Date</label>
<input type="date" id="income-date" required
class="w-full bg-slate-50 dark:bg-background-dark border border-border-light dark:border-[#233648] rounded-lg px-4 py-3 text-text-main dark:text-white focus:outline-none focus:ring-2 focus:ring-primary">
</div>
<!-- Frequency (Recurring) -->
<div>
<label for="income-frequency" class="block text-sm font-medium text-text-main dark:text-white mb-2" data-translate="income.frequency">Payment Frequency</label>
<select id="income-frequency" class="w-full bg-slate-50 dark:bg-background-dark border border-border-light dark:border-[#233648] rounded-lg px-4 py-3 text-text-main dark:text-white focus:outline-none focus:ring-2 focus:ring-primary">
<option value="once" data-translate="income.once">One-time</option>
<option value="weekly" data-translate="income.weekly">Weekly</option>
<option value="biweekly" data-translate="income.biweekly">Every 2 Weeks</option>
<option value="every4weeks" data-translate="income.every4weeks">Every 4 Weeks</option>
<option value="monthly" data-translate="income.monthly">Monthly</option>
<option value="custom" data-translate="income.custom">Custom (Freelance)</option>
</select>
</div>
<!-- Custom Frequency (shown when custom is selected) -->
<div id="custom-frequency-container" class="hidden">
<label for="income-custom-days" class="block text-sm font-medium text-text-main dark:text-white mb-2" data-translate="income.customDays">Custom Days Interval</label>
<input type="number" id="income-custom-days" min="1" placeholder="Number of days"
class="w-full bg-slate-50 dark:bg-background-dark border border-border-light dark:border-[#233648] rounded-lg px-4 py-3 text-text-main dark:text-white focus:outline-none focus:ring-2 focus:ring-primary">
<p class="text-xs text-text-muted dark:text-[#92adc9] mt-1" data-translate="income.customHelp">Enter the number of days between payments</p>
</div>
<!-- Auto-create recurring income -->
<div id="auto-create-container" class="bg-slate-50 dark:bg-[#111a22] rounded-lg p-4 border border-border-light dark:border-[#233648]">
<label class="flex items-start gap-3 cursor-pointer">
<input type="checkbox" id="income-auto-create" class="mt-1 w-4 h-4 rounded border-gray-300 text-primary focus:ring-primary focus:ring-offset-0">
<div class="flex flex-col">
<span class="text-sm font-medium text-text-main dark:text-white" data-translate="income.autoCreate">Automatically create income entries</span>
<p class="text-xs text-text-muted dark:text-[#92adc9] mt-1" data-translate="income.autoCreateHelp">When enabled, income entries will be created automatically based on the frequency. You can edit or cancel at any time.</p>
</div>
</label>
</div>
<!-- Tags -->
<div>
<label for="income-tags" class="block text-sm font-medium text-text-main dark:text-white mb-2" data-translate="form.tags">Tags (comma separated)</label>
<input type="text" id="income-tags"
class="w-full bg-slate-50 dark:bg-background-dark border border-border-light dark:border-[#233648] rounded-lg px-4 py-3 text-text-main dark:text-white focus:outline-none focus:ring-2 focus:ring-primary">
</div>
</div>
<div class="flex gap-3 mt-6">
<button type="submit" class="flex-1 bg-primary hover:bg-primary/90 text-white px-6 py-3 rounded-xl font-medium transition-all">
<span data-translate="income.save">Save Income</span>
</button>
<button type="button" onclick="closeIncomeModal()" class="flex-1 bg-slate-100 dark:bg-[#111a22] hover:bg-slate-200 dark:hover:bg-[#1a2632] text-text-main dark:text-white px-6 py-3 rounded-xl font-medium transition-all">
<span data-translate="common.cancel">Cancel</span>
</button>
</div>
</form>
</div>
</div>
<script src="{{ url_for('static', filename='js/income.js') }}"></script>
<script>
// Mobile menu toggle
document.addEventListener('DOMContentLoaded', () => {
const mobileMenuToggle = document.getElementById('mobile-menu-toggle');
const mobileMenu = document.getElementById('mobile-menu');
const mobileMenuClose = document.getElementById('mobile-menu-close');
if (mobileMenuToggle && mobileMenu) {
mobileMenuToggle.addEventListener('click', () => {
mobileMenu.classList.remove('hidden');
});
}
if (mobileMenuClose && mobileMenu) {
mobileMenuClose.addEventListener('click', () => {
mobileMenu.classList.add('hidden');
});
}
// Close mobile menu when clicking outside
if (mobileMenu) {
mobileMenu.addEventListener('click', (e) => {
if (e.target === mobileMenu) {
mobileMenu.classList.add('hidden');
}
});
}
});
</script>
{% endblock %}