diff --git a/serviceCRM/forms.py b/serviceCRM/forms.py index ed8c35f..101f322 100644 --- a/serviceCRM/forms.py +++ b/serviceCRM/forms.py @@ -1,11 +1,14 @@ from django import forms from django.utils.translation import gettext_lazy as _ -from .models import Insert +from .models import Insert, Todo class DateInput(forms.DateInput): input_type = 'date' +class TimeInput(forms.TimeInput): + input_type = 'time' + class InputForm(forms.ModelForm): class Meta: @@ -19,7 +22,7 @@ class InputForm(forms.ModelForm): 'note': "Забелешка" } widgets = { - 'date': DateInput(), + 'date': DateInput(format='%Y-%m-%d'), 'description': forms.Textarea(attrs={'rows': 3}), } @@ -36,7 +39,38 @@ class CloseForm(forms.ModelForm): 'plateno': forms.TextInput(attrs={'placeholder': 'пр. 1500 МКД'}) } +class TodoForm(forms.ModelForm): + class Meta: + model = Todo + fields = ['title', 'name', 'phone', 'scheduled_date', 'scheduled_time'] + widgets = { + 'title': forms.TextInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Опис на задача...', + }), + 'name': forms.TextInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Име на лице...' + }), + 'phone': forms.TextInput(attrs={ + 'class': 'form-control', + 'placeholder': '070-xxx-xxx' + }), + 'scheduled_date': DateInput(format='%Y-%m-%d'), + 'scheduled_time': TimeInput(format='%H:%M', attrs={ + 'class': 'form-control' + }) + } + labels = { + 'title': 'Задача', + 'name': 'Име', + 'phone': 'Телефон', + 'scheduled_date': 'Датум', + 'scheduled_time': 'Време' + } + # class EditForm(forms.ModelForm): + # class Meta: # model = Insert # fields = {"name", "phone", "description", "done"} diff --git a/serviceCRM/migrations/0012_todo.py b/serviceCRM/migrations/0012_todo.py new file mode 100644 index 0000000..8fb5083 --- /dev/null +++ b/serviceCRM/migrations/0012_todo.py @@ -0,0 +1,25 @@ +# Generated by Django 6.0.1 on 2026-01-27 19:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('serviceCRM', '0011_alter_insert_date_alter_insert_date_close_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Todo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200, verbose_name='Task')), + ('is_completed', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ], + options={ + 'ordering': ['is_completed', '-created_at'], + }, + ), + ] diff --git a/serviceCRM/migrations/0013_alter_todo_options_todo_name_todo_phone_and_more.py b/serviceCRM/migrations/0013_alter_todo_options_todo_name_todo_phone_and_more.py new file mode 100644 index 0000000..839db8e --- /dev/null +++ b/serviceCRM/migrations/0013_alter_todo_options_todo_name_todo_phone_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 6.0.1 on 2026-01-27 20:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('serviceCRM', '0012_todo'), + ] + + operations = [ + migrations.AlterModelOptions( + name='todo', + options={'ordering': ['is_completed', 'scheduled_date', 'scheduled_time', '-created_at']}, + ), + migrations.AddField( + model_name='todo', + name='name', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Име'), + ), + migrations.AddField( + model_name='todo', + name='phone', + field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Телефон'), + ), + migrations.AddField( + model_name='todo', + name='scheduled_date', + field=models.DateField(blank=True, null=True, verbose_name='Датум'), + ), + migrations.AddField( + model_name='todo', + name='scheduled_time', + field=models.TimeField(blank=True, null=True, verbose_name='Време'), + ), + ] diff --git a/serviceCRM/models.py b/serviceCRM/models.py index 731ce2c..be51436 100644 --- a/serviceCRM/models.py +++ b/serviceCRM/models.py @@ -50,6 +50,21 @@ class Insert(models.Model): def isDone(self): return self.done +class Todo(models.Model): + title = models.CharField(max_length=200, verbose_name="Task") + name = models.CharField(max_length=100, verbose_name="Име", blank=True, null=True) + phone = models.CharField(max_length=20, verbose_name="Телефон", blank=True, null=True) + scheduled_date = models.DateField(verbose_name="Датум", blank=True, null=True) + scheduled_time = models.TimeField(verbose_name="Време", blank=True, null=True) + is_completed = models.BooleanField(default=False) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return self.title + + class Meta: + ordering = ['is_completed', 'scheduled_date', 'scheduled_time', '-created_at'] + class TicketLog(models.Model): ticket = models.ForeignKey(Insert, on_delete=models.CASCADE, related_name='logs') timestamp = models.DateTimeField(auto_now_add=True) diff --git a/serviceCRM/tables.py b/serviceCRM/tables.py index 01f8e2e..1b169e4 100644 --- a/serviceCRM/tables.py +++ b/serviceCRM/tables.py @@ -7,12 +7,24 @@ from .models import Insert class InsertTable(tables.Table): actions = TemplateColumn(template_code=''' - Види - Уреди - {% if not record.done %} - Затвори - {% endif %} - ''') +
+ Види + Уреди + {% if not record.done %} + Затвори + {% endif %} +
+ ''', orderable=False) + + phone = TemplateColumn(template_code=''' + + + + + + Повикај + + ''', orderable=False) class Meta: model = Insert diff --git a/serviceCRM/templates/base.html b/serviceCRM/templates/base.html index d29999c..8e4f54c 100644 --- a/serviceCRM/templates/base.html +++ b/serviceCRM/templates/base.html @@ -33,7 +33,7 @@ - Активни налози + Активни @@ -41,25 +41,31 @@ Архива + + + + + Задачи + {% endif %} - Проверка на статус + Статус -
+
{% if user.is_authenticated %} - + -
- Здраво, {{ user.username }} + {% endif %} + +
+ + + + +
diff --git a/serviceCRM/templates/serviceCRM/nalog.html b/serviceCRM/templates/serviceCRM/nalog.html index 9d911ba..15c3f0a 100644 --- a/serviceCRM/templates/serviceCRM/nalog.html +++ b/serviceCRM/templates/serviceCRM/nalog.html @@ -2,10 +2,10 @@ {% load static %} {% block content %}
-
-
+ @@ -138,7 +138,7 @@
Налог #{{ ticket.id }}
Реф: {{ ticket.ticket_id }}
-
{{ ticket.date }}
+
{{ ticket.date|date:"d/m/y" }}
{{ ticket.get_status_display }}
diff --git a/serviceCRM/templates/serviceCRM/public_track_result.html b/serviceCRM/templates/serviceCRM/public_track_result.html index c1a8f2d..0431bd8 100644 --- a/serviceCRM/templates/serviceCRM/public_track_result.html +++ b/serviceCRM/templates/serviceCRM/public_track_result.html @@ -7,13 +7,15 @@
{% if ticket.done %} ✓ ЗАВРШЕНО + {% elif ticket.status == 'READY' %} + ✓ ГОТОВО ЗА ПОДИГАЊЕ {% else %} ⟳ ВО ИЗРАБОТКА {% endif %} @@ -63,7 +65,7 @@ Датум на прием
- {{ ticket.date }} + {{ ticket.date|date:"d/m/y" }}
@@ -82,7 +84,7 @@ Завршено на
- {{ ticket.date_close }} + {{ ticket.date_close|date:"d/m/y" }}
{% endif %} diff --git a/serviceCRM/templates/serviceCRM/todo_edit.html b/serviceCRM/templates/serviceCRM/todo_edit.html new file mode 100644 index 0000000..a485d45 --- /dev/null +++ b/serviceCRM/templates/serviceCRM/todo_edit.html @@ -0,0 +1,91 @@ +{% extends 'base.html' %} +{% load crispy_forms_tags %} + +{% block content %} + +
+ + +{% endblock %} diff --git a/serviceCRM/templates/serviceCRM/todo_list.html b/serviceCRM/templates/serviceCRM/todo_list.html new file mode 100644 index 0000000..287abb8 --- /dev/null +++ b/serviceCRM/templates/serviceCRM/todo_list.html @@ -0,0 +1,267 @@ +{% extends 'base.html' %} +{% load crispy_forms_tags %} + +{% block content %} +
+ +
+
+
+
+

Задачи

+

Управувајте со вашите дневни задачи

+
+
+
{{ completed }}/{{ total }}
+
Завршени
+
+
+ {% if total > 0 %} +
+
+ Прогрес + {{ progress }}% +
+
+
+
+
+ {% endif %} +
+ + + +
+ + +
+ +
+ + + + + + + +
+
    + {% for todo in todos %} +
  • +
    +
    + + {% if todo.is_completed %} + + + + {% else %} + + + + {% endif %} + +
    +

    + {{ todo.title }} +

    + {% if todo.name or todo.phone %} +
    + {% if todo.name %} + + + + + {{ todo.name }} + + {% endif %} + {% if todo.phone %} + + + + + {{ todo.phone }} + + {% endif %} +
    + {% endif %} +
    + {% if todo.scheduled_date %} + + + + + {{ todo.scheduled_date|date:"d/m/y" }} + + {% endif %} + {% if todo.scheduled_time %} + + + + + {{ todo.scheduled_time|time:"H:i" }} + + {% endif %} + • Креирано: {{ todo.created_at|date:"d/m/y" }} +
    +
    +
    + +
    +
  • + {% empty %} +
  • +
    + + + +

    No tasks

    +

    Get started by creating a new task.

    +
    +
  • + {% endfor %} +
+
+
+{% endblock %} diff --git a/serviceCRM/urls.py b/serviceCRM/urls.py index df379e1..f33cf76 100644 --- a/serviceCRM/urls.py +++ b/serviceCRM/urls.py @@ -39,4 +39,10 @@ urlpatterns = [ path("done/", view.Done.as_view(), name="done"), path("done//", view.Done.done_by_id, name="done_detail"), path("datatable/", view.DatatableView.as_view(), name="datatable"), + + # Todo List + path('todo/', view.todo_list, name='todo_list'), + path('todo/toggle//', view.todo_toggle, name='todo_toggle'), + path('todo/edit//', view.todo_edit, name='todo_edit'), + path('todo/delete//', view.todo_delete, name='todo_delete'), ] diff --git a/serviceCRM/views.py b/serviceCRM/views.py index e482fa9..8c20553 100644 --- a/serviceCRM/views.py +++ b/serviceCRM/views.py @@ -6,8 +6,8 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.decorators import login_required from django.contrib.auth import logout -from .forms import InputForm, CloseForm -from .models import Insert, TicketLog +from .forms import InputForm, CloseForm, TodoForm +from .models import Insert, TicketLog, Todo from .tables import DoneInsertTable, InsertTable from .filter import InsertFilter, DoneFilter @@ -45,7 +45,7 @@ class InsertNew(generic.View): TicketLog.objects.create(ticket=ticket, action="Креирано", details="Налогот е креиран во системот.") return HttpResponseRedirect(f"/ticket/{ticket.ticket_id}/") else: - form = InputForm() + form = InputForm(initial={'date': timezone.localdate()}) return render(request, InsertNew.template_name, {'form': form}) @@ -148,3 +148,86 @@ def home(request): if request.user.is_authenticated: return redirect('dashboard') return track_ticket(request) + +@login_required +def todo_list(request): + # Get filter from query parameter (default: 'active' - not done only) + filter_type = request.GET.get('filter', 'active') + + # Filter todos based on selection + if filter_type == 'completed': + todos = Todo.objects.filter(is_completed=True) + elif filter_type == 'all': + todos = Todo.objects.all() + else: # 'active' - default + todos = Todo.objects.filter(is_completed=False) + + # Calculate progress for the UI (always based on all tasks) + all_todos = Todo.objects.all() + total = all_todos.count() + completed = all_todos.filter(is_completed=True).count() + progress = int((completed / total * 100)) if total > 0 else 0 + + form = TodoForm(initial={'scheduled_date': timezone.localdate()}) + if request.method == 'POST': + form = TodoForm(request.POST) + if form.is_valid(): + form.save() + return redirect('todo_list') + + context = { + 'todos': todos, + 'form': form, + 'total': total, + 'completed': completed, + 'progress': progress, + 'filter_type': filter_type, + 'active_count': all_todos.filter(is_completed=False).count(), + } + return render(request, 'serviceCRM/todo_list.html', context) + +@login_required +def todo_edit(request, pk): + todo = get_object_or_404(Todo, pk=pk) + if request.method == 'POST': + form = TodoForm(request.POST, instance=todo) + if form.is_valid(): + form.save() + return redirect('todo_list') + else: + form = TodoForm(instance=todo) + + context = { + 'form': form, + 'todo': todo, + 'is_edit': True + } + return render(request, 'serviceCRM/todo_edit.html', context) + +@login_required +def todo_toggle(request, pk): + todo = get_object_or_404(Todo, pk=pk) + todo.is_completed = not todo.is_completed + todo.save() + return redirect('todo_list') + +@login_required +def todo_delete(request, pk): + todo = get_object_or_404(Todo, pk=pk) + todo.delete() + return redirect('todo_list') + context = {'todos': todo, 'form': form} + return render(request, 'serviceCRM/todo_list.html', context) + +@login_required +def todo_toggle(request, pk): + todo = get_object_or_404(Todo, pk=pk) + todo.is_completed = not todo.is_completed + todo.save() + return redirect('todo_list') + +@login_required +def todo_delete(request, pk): + todo = get_object_or_404(Todo, pk=pk) + todo.delete() + return redirect('todo_list')