diff --git a/tasks/admin.py b/tasks/admin.py index ce88149..3a5a3a6 100644 --- a/tasks/admin.py +++ b/tasks/admin.py @@ -1,5 +1,6 @@ from django.contrib import admin from .models import Task +from .models import Bucket class TaskAdmin(admin.ModelAdmin): list_display = ('title', 'priority', 'due_date', 'completed', 'created_at') @@ -7,4 +8,11 @@ class TaskAdmin(admin.ModelAdmin): search_fields = ('title', 'description') list_editable = ('priority', 'completed') -admin.site.register(Task, TaskAdmin) \ No newline at end of file +class BucketAdmin(admin.ModelAdmin): + list_display = ('name', 'order') + ordering = ('order',) + search_fields = ('name',) + +admin.site.register(Bucket, BucketAdmin) +admin.site.register(Task, TaskAdmin) + diff --git a/tasks/forms.py b/tasks/forms.py index 697855d..e463c84 100644 --- a/tasks/forms.py +++ b/tasks/forms.py @@ -1,7 +1,23 @@ from django import forms -from .models import Task +from .models import Task, Bucket class TaskForm(forms.ModelForm): + # Campo personalizado para permitir selecionar ou criar uma nova categoria + bucket_choice = forms.ChoiceField( + choices=[], + required=True, # Agora é obrigatório + label="Categoria", + widget=forms.Select(attrs={'class': 'form-select'}) + ) + + # Campo para nova categoria caso o usuário queira criar uma + new_bucket = forms.CharField( + max_length=100, + required=False, + label="Nova Categoria", + widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Nome da nova categoria'}) + ) + class Meta: model = Task fields = ['title', 'description', 'due_date', 'priority', 'completed'] @@ -12,6 +28,55 @@ class Meta: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + # Se já tiver um valor, formatar no formato esperado pelo input date if self.instance.due_date: - self.fields['due_date'].initial = self.instance.due_date.strftime('%Y-%m-%d') \ No newline at end of file + self.fields['due_date'].initial = self.instance.due_date.strftime('%Y-%m-%d') + + # Buscar todas as categorias e montar as opções + bucket_choices = [('', 'Selecione uma categoria')] + bucket_choices += [(str(bucket.id), bucket.name) for bucket in Bucket.objects.all().order_by('name')] + bucket_choices.append(('new', '+ Criar nova categoria')) + self.fields['bucket_choice'].choices = bucket_choices + + # Se a tarefa já tem uma categoria, selecionar como padrão + if self.instance.pk and self.instance.bucket: + self.fields['bucket_choice'].initial = str(self.instance.bucket.id) + + def clean(self): + cleaned_data = super().clean() + bucket_choice = cleaned_data.get('bucket_choice') + new_bucket = cleaned_data.get('new_bucket') + + # Verificar se uma categoria foi selecionada + if not bucket_choice: + self.add_error('bucket_choice', 'Por favor, selecione uma categoria ou crie uma nova.') + + # Verificar se a opção de criar nova categoria foi selecionada mas nenhum nome foi fornecido + elif bucket_choice == 'new' and not new_bucket: + self.add_error('new_bucket', 'Por favor, informe um nome para a nova categoria.') + + return cleaned_data + + def save(self, commit=True): + instance = super().save(commit=False) + + bucket_choice = self.cleaned_data.get('bucket_choice') + new_bucket = self.cleaned_data.get('new_bucket') + + # Se selecionou criar nova categoria + if bucket_choice == 'new' and new_bucket: + # Cria ou busca uma categoria com esse nome + bucket, created = Bucket.objects.get_or_create(name=new_bucket) + instance.bucket = bucket + # Se selecionou uma categoria existente + elif bucket_choice and bucket_choice != 'new': + instance.bucket = Bucket.objects.get(id=bucket_choice) + # Este caso não deveria acontecer devido à validação, mas apenas por segurança + else: + instance.bucket = None + + if commit: + instance.save() + + return instance \ No newline at end of file diff --git a/tasks/migrations/0003_include_bucket_schema.py b/tasks/migrations/0003_include_bucket_schema.py new file mode 100644 index 0000000..fb62524 --- /dev/null +++ b/tasks/migrations/0003_include_bucket_schema.py @@ -0,0 +1,93 @@ +# Generated by Django 5.2.1 on 2025-05-10 16:38 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = False + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Bucket", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=100, unique=True, verbose_name="Nome"), + ), + ("order", models.IntegerField(default=0, verbose_name="Ordem")), + ], + options={ + "verbose_name": "Categoria", + "verbose_name_plural": "Categorias", + "ordering": ["order", "name"], + }, + ), + migrations.CreateModel( + name="Task", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=200, verbose_name="Título")), + ("description", models.TextField(blank=True, verbose_name="Descrição")), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Data de Criação" + ), + ), + ( + "due_date", + models.DateTimeField( + blank=True, null=True, verbose_name="Data de Vencimento" + ), + ), + ( + "priority", + models.IntegerField( + choices=[(1, "Baixa"), (2, "Média"), (3, "Alta")], + default=2, + verbose_name="Prioridade", + ), + ), + ( + "completed", + models.BooleanField(default=False, verbose_name="Concluída"), + ), + ( + "bucket", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="tasks.bucket", + verbose_name="Categoria", + ), + ), + ], + options={ + "verbose_name": "Tarefa", + "verbose_name_plural": "Tarefas", + "ordering": ["-priority", "due_date"], + }, + ), + ] diff --git a/tasks/models.py b/tasks/models.py index d441723..7920c80 100644 --- a/tasks/models.py +++ b/tasks/models.py @@ -1,6 +1,17 @@ from django.db import models -# Create your models here. +class Bucket(models.Model): + name = models.CharField(max_length=100, unique=True, verbose_name='Nome') + order = models.IntegerField(default=0, verbose_name='Ordem') + + def __str__(self): + return self.name + + class Meta: + ordering = ['order', 'name'] + verbose_name = 'Categoria' + verbose_name_plural = 'Categorias' + class Task(models.Model): PRIORITY_CHOICES = [ (1, 'Baixa'), @@ -15,6 +26,8 @@ class Task(models.Model): priority = models.IntegerField(choices=PRIORITY_CHOICES, default=2, verbose_name='Prioridade') completed = models.BooleanField(default=False, verbose_name='Concluída') + bucket = models.ForeignKey(Bucket, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='Categoria') + def __str__(self): return self.title diff --git a/tasks/templates/tasks/base.html b/tasks/templates/tasks/base.html index 9b85745..aebf006 100644 --- a/tasks/templates/tasks/base.html +++ b/tasks/templates/tasks/base.html @@ -1,212 +1,269 @@ -
- - + + +