diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index cd6f855..40d07e7 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.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..bc7a61e 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 9aa598b..9f1dce4 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 6867ddf..3977a45 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..a9113b6 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,3 +1,13 @@ from django.contrib import admin +from .models import Category, Expense -# Register your models here. +@admin.register(Category) +class CategoryAdmin(admin.ModelAdmin): + list_display = ('name',) + search_fields = ('name',) + +@admin.register(Expense) +class ExpenseAdmin(admin.ModelAdmin): + list_display = ('date', 'category', 'amount', 'currency', 'payer') + list_filter = ('date', 'category', 'currency', 'payer') + search_fields = ('description',) \ No newline at end of file diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..1653d3d --- /dev/null +++ b/core/forms.py @@ -0,0 +1,7 @@ +from django import forms +from .models import Expense + +class ExpenseForm(forms.ModelForm): + class Meta: + model = Expense + fields = ['payer', 'amount', 'currency', 'category', 'description', 'date'] diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..cf9581d --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 5.2.7 on 2026-01-10 16:57 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + 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, unique=True)), + ], + options={ + 'verbose_name_plural': 'Categories', + }, + ), + migrations.CreateModel( + name='Expense', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.DecimalField(decimal_places=2, max_digits=10)), + ('currency', models.CharField(choices=[('COP', 'Colombian Pesos'), ('EUR', 'Euros'), ('USD', 'US Dollars')], max_length=3)), + ('date', models.DateField()), + ('description', models.TextField(blank=True)), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='core.category')), + ('payer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] 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..5c03095 Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 71a8362..7780abd 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,28 @@ from django.db import models +from django.contrib.auth.models import User -# Create your models here. +class Category(models.Model): + name = models.CharField(max_length=100, unique=True) + + def __str__(self): + return self.name + + class Meta: + verbose_name_plural = "Categories" + +class Expense(models.Model): + CURRENCY_CHOICES = [ + ('COP', 'Colombian Pesos'), + ('EUR', 'Euros'), + ('USD', 'US Dollars'), + ] + + amount = models.DecimalField(max_digits=10, decimal_places=2) + currency = models.CharField(max_length=3, choices=CURRENCY_CHOICES) + category = models.ForeignKey(Category, on_delete=models.PROTECT) + payer = models.ForeignKey(User, on_delete=models.CASCADE) + date = models.DateField() + description = models.TextField(blank=True) + + def __str__(self): + return f'{self.amount} {self.currency} - {self.category} - {self.date}' \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..38d947a 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,9 +1,10 @@ - + - {% block title %}Knowledge Base{% endblock %} + + {% block title %}Gastos Familiares{% endblock %} {% if project_description %} @@ -13,13 +14,50 @@ {% endif %} + + + + + + {% load static %} + {% block head %}{% endblock %} - {% block content %}{% endblock %} + + +
+ {% block content %}{% endblock %} +
+ + + + diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..d2e428d 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,102 @@ -{% extends "base.html" %} +{% extends 'base.html' %} -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% block title %}Inicio - Gastos Familiares{% endblock %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+
+
+

Agregar Nuevo Gasto

+
+ {% csrf_token %} +
+ {{ form.payer.label_tag }} + {{ form.payer }} +
+
+ {{ form.amount.label_tag }} + {{ form.amount }} +
+
+ {{ form.currency.label_tag }} + {{ form.currency }} +
+
+ {{ form.category.label_tag }} + {{ form.category }} +
+
+ {{ form.description.label_tag }} + {{ form.description }} +
+
+ {{ form.date.label_tag }} + {{ form.date }} +
+ +
+
+
+
+
+

Tus Gastos Recientes

+ +
+
+ +
+
+ +
+
+ +
+
+ + {% if expenses %} +
    + {% for expense in expenses %} +
  • +
    + {{ expense.category }} - {{ expense.description }} + ({{ expense.date|date:"d/m/Y" }}) +
    + ${{ expense.amount }} +
  • + {% endfor %} +
+ {% else %} + + {% endif %} +
+
+

Análisis de Gastos

+
+
+
Total de Gastos
+

${{ total_expenses|floatformat:2 }}

+
+
+ +
Gastos por Categoría
+
    + {% for item in category_summary %} +
  • + {{ item.category__name }} + ${{ item.total|floatformat:2 }} +
  • + {% endfor %} +
+
+
-

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 + +{% endblock %} diff --git a/core/views.py b/core/views.py index c9aed12..68a0713 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,38 @@ -import os -import platform - -from django import get_version as django_version -from django.shortcuts import render -from django.utils import timezone - +from django.shortcuts import render, redirect +from .forms import ExpenseForm +from .models import Expense, Category +from django.db.models import Sum 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() + """Render the landing screen and handle expense form submission.""" + if request.method == 'POST': + form = ExpenseForm(request.POST) + if form.is_valid(): + form.save() + return redirect('home') + else: + form = ExpenseForm() + + expenses = Expense.objects.all() + + # Filtering logic + category_id = request.GET.get('category') + if category_id: + expenses = expenses.filter(category_id=category_id) + + expenses = expenses.order_by('-date') + + # Analysis + total_expenses = expenses.aggregate(Sum('amount'))['amount__sum'] or 0 + category_summary = expenses.values('category__name').annotate(total=Sum('amount')).order_by('-total') + + categories = Category.objects.all() + + return render(request, "core/index.html", { + 'form': form, + 'expenses': expenses, + 'categories': categories, + 'total_expenses': total_expenses, + 'category_summary': category_summary, + }) - 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", ""), - } - return render(request, "core/index.html", context) diff --git a/static/css/custom.css b/static/css/custom.css index 925f6ed..b095735 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -1,4 +1,58 @@ -/* Custom styles for the application */ -body { - font-family: system-ui, -apple-system, sans-serif; +:root { + --bs-primary: #0D47A1; + --bs-secondary: #42A5F5; + --bs-accent: #FFC107; + --bs-background: #F5F5F5; + --bs-text: #212121; } + +body { + font-family: 'Roboto', sans-serif; + background-color: var(--bs-background); + color: var(--bs-text); + display: flex; + flex-direction: column; + min-height: 100vh; +} + +h1, h2, h3, h4, h5, h6, .navbar-brand { + font-family: 'Montserrat', sans-serif; +} + +.bg-primary { + background-color: var(--bs-primary) !important; +} + +.navbar-brand { + font-weight: 700; +} + +.hero-section { + background: linear-gradient(45deg, var(--bs-primary), var(--bs-secondary)); + color: white; + padding: 100px 0; + text-align: center; +} + +.hero-section h1 { + font-weight: 700; + font-size: 3.5rem; +} + +.hero-section .btn-accent { + background-color: var(--bs-accent); + color: var(--bs-text); + font-weight: 700; + padding: 15px 30px; + font-size: 1.2rem; + border-radius: 50px; + transition: transform 0.2s; +} + +.hero-section .btn-accent:hover { + transform: scale(1.05); +} + +.content-section { + padding: 60px 0; +} \ No newline at end of file diff --git a/staticfiles/css/custom.css b/staticfiles/css/custom.css index 108056f..b095735 100644 --- a/staticfiles/css/custom.css +++ b/staticfiles/css/custom.css @@ -1,21 +1,58 @@ - :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); + --bs-primary: #0D47A1; + --bs-secondary: #42A5F5; + --bs-accent: #FFC107; + --bs-background: #F5F5F5; + --bs-text: #212121; } + body { - margin: 0; - 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; + font-family: 'Roboto', sans-serif; + background-color: var(--bs-background); + color: var(--bs-text); + display: flex; + flex-direction: column; + min-height: 100vh; } + +h1, h2, h3, h4, h5, h6, .navbar-brand { + font-family: 'Montserrat', sans-serif; +} + +.bg-primary { + background-color: var(--bs-primary) !important; +} + +.navbar-brand { + font-weight: 700; +} + +.hero-section { + background: linear-gradient(45deg, var(--bs-primary), var(--bs-secondary)); + color: white; + padding: 100px 0; + text-align: center; +} + +.hero-section h1 { + font-weight: 700; + font-size: 3.5rem; +} + +.hero-section .btn-accent { + background-color: var(--bs-accent); + color: var(--bs-text); + font-weight: 700; + padding: 15px 30px; + font-size: 1.2rem; + border-radius: 50px; + transition: transform 0.2s; +} + +.hero-section .btn-accent:hover { + transform: scale(1.05); +} + +.content-section { + padding: 60px 0; +} \ No newline at end of file