diff --git a/config/__pycache__/__init__.cpython-311.pyc b/config/__pycache__/__init__.cpython-311.pyc index 423a636..033bbd2 100644 Binary files a/config/__pycache__/__init__.cpython-311.pyc and b/config/__pycache__/__init__.cpython-311.pyc differ diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 96bce55..cfe6ff2 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 0b85e94..80ed711 100644 Binary files a/config/__pycache__/urls.cpython-311.pyc and b/config/__pycache__/urls.cpython-311.pyc differ diff --git a/config/__pycache__/wsgi.cpython-311.pyc b/config/__pycache__/wsgi.cpython-311.pyc index 9c49e09..14e1012 100644 Binary files a/config/__pycache__/wsgi.cpython-311.pyc and b/config/__pycache__/wsgi.cpython-311.pyc differ diff --git a/core/__pycache__/__init__.cpython-311.pyc b/core/__pycache__/__init__.cpython-311.pyc index 74b1112..b38c29f 100644 Binary files a/core/__pycache__/__init__.cpython-311.pyc and b/core/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index a5ed392..e7f55d3 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/apps.cpython-311.pyc b/core/__pycache__/apps.cpython-311.pyc index 6f131d4..7b8c00f 100644 Binary files a/core/__pycache__/apps.cpython-311.pyc and b/core/__pycache__/apps.cpython-311.pyc differ diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc index 75bf223..7787788 100644 Binary files a/core/__pycache__/context_processors.cpython-311.pyc and b/core/__pycache__/context_processors.cpython-311.pyc differ diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000..7a5501f Binary files /dev/null and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index e061640..837f00b 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 5a69659..4fbc8c4 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2a36fd6..da2864b 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index 8c38f3f..44d25c1 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,3 +1,15 @@ from django.contrib import admin +from .models import Category, Transaction -# Register your models here. +@admin.register(Category) +class CategoryAdmin(admin.ModelAdmin): + list_display = ('name', 'type', 'icon') + list_filter = ('type',) + search_fields = ('name',) + +@admin.register(Transaction) +class TransactionAdmin(admin.ModelAdmin): + list_display = ('date', 'type', 'amount', 'category', 'description') + list_filter = ('type', 'category', 'date') + search_fields = ('description',) + date_hierarchy = 'date' \ No newline at end of file diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..43e7886 --- /dev/null +++ b/core/forms.py @@ -0,0 +1,18 @@ +from django import forms +from .models import Transaction, Category + +class TransactionForm(forms.ModelForm): + class Meta: + model = Transaction + fields = ['amount', 'type', 'category', 'date', 'description'] + widgets = { + 'date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}), + 'amount': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'placeholder': '0.00'}), + 'type': forms.Select(attrs={'class': 'form-select'}), + 'category': forms.Select(attrs={'class': 'form-select'}), + 'description': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'What was this for?'}), + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['category'].queryset = Category.objects.all() diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..65ff391 --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,40 @@ +# Generated by Django 5.2.7 on 2026-02-01 13:44 + +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Category', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('type', models.CharField(choices=[('income', 'Income'), ('expense', 'Expense')], default='expense', max_length=10)), + ('icon', models.CharField(blank=True, help_text='FontAwesome icon name (e.g., fa-utensils)', max_length=50, null=True)), + ], + options={ + 'verbose_name_plural': 'Categories', + }, + ), + migrations.CreateModel( + name='Transaction', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.DecimalField(decimal_places=2, max_digits=10)), + ('date', models.DateField(default=django.utils.timezone.now)), + ('description', models.CharField(blank=True, max_length=255)), + ('type', models.CharField(choices=[('income', 'Income'), ('expense', 'Expense')], default='expense', max_length=10)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='transactions', to='core.category')), + ], + ), + ] diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000..6f3bfa6 Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/__init__.cpython-311.pyc b/core/migrations/__pycache__/__init__.cpython-311.pyc index 9c833c8..5fe63f4 100644 Binary files a/core/migrations/__pycache__/__init__.cpython-311.pyc and b/core/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 71a8362..f66a94e 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,32 @@ from django.db import models +from django.utils import timezone -# Create your models here. +class Category(models.Model): + TRANSACTION_TYPES = [ + ('income', 'Income'), + ('expense', 'Expense'), + ] + name = models.CharField(max_length=100) + type = models.CharField(max_length=10, choices=TRANSACTION_TYPES, default='expense') + icon = models.CharField(max_length=50, blank=True, null=True, help_text="FontAwesome icon name (e.g., fa-utensils)") + + class Meta: + verbose_name_plural = "Categories" + + def __str__(self): + return f"{self.name} ({self.get_type_display()})" + +class Transaction(models.Model): + TYPES = [ + ('income', 'Income'), + ('expense', 'Expense'), + ] + amount = models.DecimalField(max_digits=10, decimal_places=2) + category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='transactions') + date = models.DateField(default=timezone.now) + description = models.CharField(max_length=255, blank=True) + type = models.CharField(max_length=10, choices=TYPES, default='expense') + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"{self.get_type_display()}: {self.amount} - {self.description[:20]}" \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..b4cdc37 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,25 +1,72 @@ - - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {% block title %}BudgetTracker{% endblock %} + + {% if project_description %} + + + + {% endif %} + {% if project_image_url %} + + + {% endif %} + + {% load static %} + + + + + + + + {% block head %}{% endblock %} - - {% block content %}{% endblock %} - + - +
+ {% block content %}{% endblock %} +
+ + + + + + {% block scripts %}{% endblock %} + + \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..7799906 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,147 @@ -{% extends "base.html" %} - -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% extends 'base.html' %} +{% load static %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+
+
+
+

Hello! Here's your budget overview.

+

Tracking your finances for {{ current_month }}

+
+
+ +
+
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
- -{% endblock %} \ No newline at end of file + + +
+ +
+
+
+
+
+ +
+ Total Balance +
+

+ ${{ balance|floatformat:2 }} +

+
+
+
+
+
+
+ +
+ Monthly Income +
+

${{ monthly_income|floatformat:2 }}

+
+
+
+
+
+
+ +
+ Monthly Expenses +
+

${{ monthly_expense|floatformat:2 }}

+
+
+
+ + +
+
+
+

Recent Transactions

+ + View All + +
+ +
+ + + {% for transaction in recent_transactions %} + + + + + + {% empty %} + + + + {% endfor %} + +
+
{{ transaction.date|date:"M d" }}
+ {{ transaction.date|date:"Y" }} +
+
{{ transaction.description|default:"No description" }}
+ {{ transaction.category.name|default:"Uncategorized" }} +
+
+ {% if transaction.type == 'income' %}+{% else %}-{% endif %}${{ transaction.amount|floatformat:2 }} +
+ {{ transaction.get_type_display }} +
+ Empty +

No transactions yet. Start by adding one!

+
+
+
+
+
+ + + +{% endblock %} diff --git a/core/views.py b/core/views.py index c9aed12..55e35b3 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,40 @@ -import os -import platform - -from django import get_version as django_version -from django.shortcuts import render +from django.shortcuts import render, redirect +from django.db.models import Sum from django.utils import timezone - +from .models import Transaction, Category +from .forms import TransactionForm +import datetime def home(request): - """Render the landing screen with loader and environment details.""" - host_name = request.get_host().lower() - agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" - now = timezone.now() - + today = timezone.now().date() + first_day_of_month = today.replace(day=1) + + # Calculate stats + total_income = Transaction.objects.filter(type='income').aggregate(Sum('amount'))['amount__sum'] or 0 + total_expense = Transaction.objects.filter(type='expense').aggregate(Sum('amount'))['amount__sum'] or 0 + balance = total_income - total_expense + + monthly_income = Transaction.objects.filter(type='income', date__gte=first_day_of_month).aggregate(Sum('amount'))['amount__sum'] or 0 + monthly_expense = Transaction.objects.filter(type='expense', date__gte=first_day_of_month).aggregate(Sum('amount'))['amount__sum'] or 0 + + recent_transactions = Transaction.objects.select_related('category').order_by('-date', '-created_at')[:10] + + if request.method == 'POST': + form = TransactionForm(request.POST) + if form.is_valid(): + form.save() + return redirect('home') + else: + form = TransactionForm() + context = { - "project_name": "New Style", - "agent_brand": agent_brand, - "django_version": django_version(), - "python_version": platform.python_version(), - "current_time": now, - "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), + 'total_income': total_income, + 'total_expense': total_expense, + 'balance': balance, + 'monthly_income': monthly_income, + 'monthly_expense': monthly_expense, + 'recent_transactions': recent_transactions, + 'form': form, + 'current_month': today.strftime('%B %Y'), } - return render(request, "core/index.html", context) + return render(request, 'core/index.html', context) \ No newline at end of file diff --git a/static/css/custom.css b/static/css/custom.css index 925f6ed..b78544d 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -1,4 +1,86 @@ -/* Custom styles for the application */ -body { - font-family: system-ui, -apple-system, sans-serif; +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@600;700&display=swap'); + +:root { + --bg-color: #F8FAFC; + --slate-800: #1E293B; + --slate-500: #64748B; + --emerald: #10B981; + --rose: #F43F5E; + --indigo: #6366F1; + --violet: #8B5CF6; } + +body { + background-color: var(--bg-color); + font-family: 'Inter', sans-serif; + color: var(--slate-800); +} + +h1, h2, h3, .h1, .h2, .h3 { + font-family: 'Outfit', sans-serif; + font-weight: 700; +} + +.hero-gradient { + background: linear-gradient(135deg, var(--indigo) 0%, var(--violet) 100%); + color: white; + padding: 3rem 0; + border-radius: 0 0 2rem 2rem; + margin-bottom: -4rem; +} + +.stats-card { + background: rgba(255, 255, 255, 0.8); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 1.25rem; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; +} + +.stats-card:hover { + transform: translateY(-2px); +} + +.income-text { color: var(--emerald); } +.expense-text { color: var(--rose); } + +.btn-primary-gradient { + background: linear-gradient(135deg, var(--indigo) 0%, var(--violet) 100%); + border: none; + color: white; + font-weight: 600; + padding: 0.75rem 1.5rem; + border-radius: 0.75rem; +} + +.btn-primary-gradient:hover { + opacity: 0.9; + color: white; +} + +.transaction-row { + border-left: 4px solid transparent; + transition: all 0.2s ease; +} + +.transaction-row.income { border-left-color: var(--emerald); } +.transaction-row.expense { border-left-color: var(--rose); } + +.table { + border-collapse: separate; + border-spacing: 0 0.5rem; +} + +.table tr { + background: white; + border-radius: 0.75rem; +} + +.table td { + padding: 1rem; + vertical-align: middle; +} + +.table td:first-child { border-radius: 0.75rem 0 0 0.75rem; } +.table td:last-child { border-radius: 0 0.75rem 0.75rem 0; } \ No newline at end of file diff --git a/staticfiles/css/custom.css b/staticfiles/css/custom.css index 108056f..b78544d 100644 --- a/staticfiles/css/custom.css +++ b/staticfiles/css/custom.css @@ -1,21 +1,86 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@600;700&display=swap'); :root { - --bg-color-start: #6a11cb; - --bg-color-end: #2575fc; - --text-color: #ffffff; - --card-bg-color: rgba(255, 255, 255, 0.01); - --card-border-color: rgba(255, 255, 255, 0.1); + --bg-color: #F8FAFC; + --slate-800: #1E293B; + --slate-500: #64748B; + --emerald: #10B981; + --rose: #F43F5E; + --indigo: #6366F1; + --violet: #8B5CF6; } + body { - margin: 0; + background-color: var(--bg-color); font-family: 'Inter', sans-serif; - background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end)); - color: var(--text-color); - display: flex; - justify-content: center; - align-items: center; - min-height: 100vh; - text-align: center; - overflow: hidden; - position: relative; + color: var(--slate-800); } + +h1, h2, h3, .h1, .h2, .h3 { + font-family: 'Outfit', sans-serif; + font-weight: 700; +} + +.hero-gradient { + background: linear-gradient(135deg, var(--indigo) 0%, var(--violet) 100%); + color: white; + padding: 3rem 0; + border-radius: 0 0 2rem 2rem; + margin-bottom: -4rem; +} + +.stats-card { + background: rgba(255, 255, 255, 0.8); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 1.25rem; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; +} + +.stats-card:hover { + transform: translateY(-2px); +} + +.income-text { color: var(--emerald); } +.expense-text { color: var(--rose); } + +.btn-primary-gradient { + background: linear-gradient(135deg, var(--indigo) 0%, var(--violet) 100%); + border: none; + color: white; + font-weight: 600; + padding: 0.75rem 1.5rem; + border-radius: 0.75rem; +} + +.btn-primary-gradient:hover { + opacity: 0.9; + color: white; +} + +.transaction-row { + border-left: 4px solid transparent; + transition: all 0.2s ease; +} + +.transaction-row.income { border-left-color: var(--emerald); } +.transaction-row.expense { border-left-color: var(--rose); } + +.table { + border-collapse: separate; + border-spacing: 0 0.5rem; +} + +.table tr { + background: white; + border-radius: 0.75rem; +} + +.table td { + padding: 1rem; + vertical-align: middle; +} + +.table td:first-child { border-radius: 0.75rem 0 0 0.75rem; } +.table td:last-child { border-radius: 0 0.75rem 0.75rem 0; } \ No newline at end of file