diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc
index 20f0cc2..42269e6 100644
Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ
diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc
index cd02fcf..8072a30 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 0580716..18d5687 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 53fe095..8f27c3c 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 19afb57..fab9d55 100644
--- a/core/admin.py
+++ b/core/admin.py
@@ -1,5 +1,8 @@
from django.contrib import admin
-from .models import Article, TodoItem
+from .models import Article, TodoItem, Setting, Conversation, Message
admin.site.register(Article)
-admin.site.register(TodoItem)
\ No newline at end of file
+admin.site.register(TodoItem)
+admin.site.register(Setting)
+admin.site.register(Conversation)
+admin.site.register(Message)
diff --git a/core/migrations/0006_setting.py b/core/migrations/0006_setting.py
new file mode 100644
index 0000000..cc29edb
--- /dev/null
+++ b/core/migrations/0006_setting.py
@@ -0,0 +1,21 @@
+# Generated by Django 5.2.7 on 2025-11-20 10:50
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0005_alter_message_options_remove_message_is_from_user_and_more'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Setting',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.CharField(max_length=255, unique=True)),
+ ('value', models.TextField()),
+ ],
+ ),
+ ]
diff --git a/core/migrations/__pycache__/0006_setting.cpython-311.pyc b/core/migrations/__pycache__/0006_setting.cpython-311.pyc
new file mode 100644
index 0000000..49bfd5f
Binary files /dev/null and b/core/migrations/__pycache__/0006_setting.cpython-311.pyc differ
diff --git a/core/models.py b/core/models.py
index b9cb4ae..01752e5 100644
--- a/core/models.py
+++ b/core/models.py
@@ -42,4 +42,11 @@ class Message(models.Model):
ordering = ['created_at']
def __str__(self):
- return f"Message from {self.get_sender_display()} at {self.created_at}"
\ No newline at end of file
+ return f"Message from {self.get_sender_display()} at {self.created_at}"
+
+class Setting(models.Model):
+ key = models.CharField(max_length=255, unique=True)
+ value = models.TextField()
+
+ def __str__(self):
+ return self.key
diff --git a/core/templates/base.html b/core/templates/base.html
index 905e789..bdd75c5 100644
--- a/core/templates/base.html
+++ b/core/templates/base.html
@@ -30,6 +30,9 @@
Chat
+
+ Settings
+
diff --git a/core/templates/core/settings.html b/core/templates/core/settings.html
new file mode 100644
index 0000000..f4a5f3d
--- /dev/null
+++ b/core/templates/core/settings.html
@@ -0,0 +1,18 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/core/urls.py b/core/urls.py
index 1a7b1a1..b1dc985 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -12,4 +12,5 @@ urlpatterns = [
path('chat/', views.chat_view, name='chat'),
path('chat//', views.chat_view, name='chat_detail'),
path('cleanup_tasks/', views.cleanup_tasks, name='cleanup_tasks'),
+ path('settings/', views.settings_view, name='settings'),
]
\ No newline at end of file
diff --git a/core/views.py b/core/views.py
index 73e0364..29c919f 100644
--- a/core/views.py
+++ b/core/views.py
@@ -3,7 +3,7 @@ from django.http import JsonResponse
from django.views.decorators.http import require_POST
import json
import logging
-from .models import Article, TodoItem, Conversation, Message
+from .models import Article, TodoItem, Conversation, Message, Setting
from .forms import TodoItemForm
import time
from ai.local_ai_api import LocalAIApi
@@ -170,9 +170,15 @@ def chat_view(request, conversation_id=None):
history.append({"role": role, "content": msg.content})
try:
+ custom_instructions, created = Setting.objects.get_or_create(
+ key='custom_instructions',
+ defaults={'value': ''}
+ )
+ custom_instructions_text = custom_instructions.value + '\n\n' if custom_instructions.value else ''
+
system_message = {
"role": "system",
- "content": '''You are a project management assistant. To communicate with the user, you MUST use the `send_message` command.
+ "content": custom_instructions_text + '''You are a project management assistant. To communicate with the user, you MUST use the `send_message` command.
**Commands must be in a specific JSON format.** Your response must be a JSON object with the following structure:
@@ -333,3 +339,23 @@ def chat_view(request, conversation_id=None):
'selected_conversation': selected_conversation,
'timestamp': int(time.time()),
})
+
+def conversation_list(request):
+ conversations = Conversation.objects.order_by('-created_at')
+ return render(request, 'core/conversation_list.html', {'conversation_list': conversations})
+
+def settings_view(request):
+ # Get or create the custom_instructions setting
+ custom_instructions, created = Setting.objects.get_or_create(
+ key='custom_instructions',
+ defaults={'value': ''}
+ )
+
+ if request.method == 'POST':
+ custom_instructions.value = request.POST.get('custom_instructions', '')
+ custom_instructions.save()
+ return redirect('core:settings')
+
+ return render(request, 'core/settings.html', {
+ 'custom_instructions': custom_instructions
+ })
\ No newline at end of file
diff --git a/static/css/custom.css b/static/css/custom.css
index 551c68f..293163a 100644
--- a/static/css/custom.css
+++ b/static/css/custom.css
@@ -227,4 +227,71 @@ body {
position: absolute;
top: 10px;
right: 10px;
-}
\ No newline at end of file
+}
+
+/* Kanban Board Styles */
+.kanban-board-container {
+ width: 100%;
+ overflow-x: auto;
+ padding: 1rem;
+}
+
+.kanban-board {
+ display: flex;
+ gap: 1rem;
+ min-width: max-content; /* Ensure board expands horizontally */
+}
+
+.kanban-column {
+ flex: 1 1 300px; /* Flex-grow, flex-shrink, and basis */
+ min-width: 300px;
+ max-width: 320px;
+ background-color: #f0f2f5;
+ border-radius: 0.5rem;
+ display: flex;
+ flex-direction: column;
+ max-height: calc(100vh - 250px); /* Adjust based on your layout */
+}
+
+.kanban-column h2 {
+ position: sticky;
+ top: 0;
+ z-index: 2;
+}
+
+.kanban-cards {
+ overflow-y: auto;
+ flex-grow: 1;
+ min-height: 150px; /* Ensure drop zone is available even when empty */
+}
+
+.kanban-card {
+ cursor: grab;
+ transition: background-color 0.2s, box-shadow 0.2s;
+}
+
+.kanban-card:hover {
+ background-color: #f8f9fa;
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+}
+
+.kanban-card .btn-close {
+ transition: opacity 0.2s;
+ opacity: 0;
+}
+
+.kanban-card:hover .btn-close {
+ opacity: 1;
+}
+
+/* For the drag-and-drop placeholder */
+.sortable-ghost {
+ background-color: #e9ecef;
+ border: 2px dashed #ced4da;
+}
+
+.sortable-drag {
+ opacity: 1 !important; /* Override Sortable.js default opacity */
+ box-shadow: 0 8px 16px rgba(0,0,0,0.2);
+ transform: rotate(3deg);
+}
diff --git a/static/css/custom.css.bak b/static/css/custom.css.bak
new file mode 100644
index 0000000..551c68f
--- /dev/null
+++ b/static/css/custom.css.bak
@@ -0,0 +1,230 @@
+/* General App Body & Layout */
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ background-color: #f8f9fa;
+ color: #212529;
+}
+
+/* Main Chat Layout */
+.chat-container {
+ display: flex;
+ height: calc(100vh - 120px); /* Adjusted for header/footer */
+ width: 100%;
+ background-color: #fff;
+ border: 1px solid #dee2e6;
+ border-radius: 0.5rem;
+}
+
+/* Sidebar Styles */
+.chat-sidebar {
+ width: 280px;
+ background-color: #f8f9fa;
+ border-right: 1px solid #dee2e6;
+ display: flex;
+ flex-direction: column;
+ padding: 1rem;
+ flex-shrink: 0;
+}
+
+.new-chat-form {
+ display: flex;
+ margin-bottom: 1rem;
+}
+
+.new-chat-form input {
+ flex: 1;
+ padding: 0.75rem;
+ border: 1px solid #ced4da;
+ border-radius: 0.375rem 0 0 0.375rem;
+ font-size: 0.9rem;
+}
+
+.new-chat-form button {
+ padding: 0.75rem 1rem;
+ background-color: #0d6efd;
+ color: #fff;
+ border: 1px solid #0d6efd;
+ border-radius: 0 0.375rem 0.375rem 0;
+ cursor: pointer;
+}
+
+.conversation-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ overflow-y: auto;
+}
+
+.conversation-list a {
+ display: block;
+ padding: 0.75rem 1rem;
+ color: #495057;
+ text-decoration: none;
+ border-radius: 0.375rem;
+ margin-bottom: 0.25rem;
+}
+
+.conversation-list a:hover {
+ background-color: #e9ecef;
+}
+
+.conversation-list a.active {
+ background-color: #0d6efd;
+ color: #fff;
+}
+
+/* Main Content Area */
+.chat-main {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ background-color: #ffffff;
+ position: relative; /* Needed for loader overlay */
+}
+
+/* Chat Header */
+.chat-header {
+ padding: 1rem 1.5rem;
+ border-bottom: 1px solid #dee2e6;
+ flex-shrink: 0;
+}
+
+.chat-header h3 {
+ margin: 0;
+ font-size: 1.1rem;
+}
+
+/* Message Area */
+.chat-messages {
+ flex: 1;
+ padding: 1.5rem;
+ overflow-y: auto;
+}
+
+.message {
+ margin-bottom: 1rem;
+ display: flex;
+}
+
+.message-content {
+ max-width: 80%;
+ padding: 0.75rem 1rem;
+ border-radius: 0.5rem;
+ line-height: 1.5;
+}
+
+.message.user {
+ justify-content: flex-end;
+}
+
+.message.user .message-content {
+ background-color: #0d6efd;
+ color: #fff;
+}
+
+.message.ai .message-content {
+ background-color: #e9ecef;
+ color: #343a40;
+}
+
+.message-author {
+ font-weight: bold;
+ font-size: 0.8rem;
+ margin-bottom: 0.25rem;
+}
+
+/* Message Input Form */
+.chat-form-container {
+ padding: 1rem 1.5rem;
+ border-top: 1px solid #dee2e6;
+ background-color: #f8f9fa;
+}
+
+.chat-form {
+ display: flex;
+}
+
+.chat-form textarea {
+ flex: 1;
+ padding: 0.75rem;
+ border: 1px solid #ced4da;
+ border-radius: 0.375rem;
+ font-size: 1rem;
+ resize: none;
+}
+
+.chat-form button {
+ margin-left: 1rem;
+ padding: 0.75rem 1.5rem;
+ background-color: #0d6efd;
+ color: #fff;
+ border: none;
+ border-radius: 0.375rem;
+ cursor: pointer;
+}
+
+/* Empty State for Chat */
+.no-conversation-selected {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ color: #6c757d;
+}
+
+/* Loader Styles */
+.loader-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(255, 255, 255, 0.8);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 10;
+}
+
+.loader {
+ border: 5px solid #f3f3f3; /* Light grey */
+ border-top: 5px solid #0d6efd; /* Blue */
+ border-radius: 50%;
+ width: 50px;
+ height: 50px;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* System and AI Command Messages */
+.message.system .message-content {
+ background-color: #f8d7da;
+ color: #721c24;
+ border: 1px solid #f5c6cb;
+}
+
+.message.ai_command .message-content {
+ background-color: #d1ecf1;
+ color: #0c5460;
+ border: 1px solid #bee5eb;
+}
+
+.message.ai_command .message-content pre {
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ margin: 0;
+}
+
+.kanban-card .card-body {
+ position: relative;
+}
+
+.delete-task-form {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+}
\ No newline at end of file
diff --git a/staticfiles/css/custom.css b/staticfiles/css/custom.css
index 551c68f..293163a 100644
--- a/staticfiles/css/custom.css
+++ b/staticfiles/css/custom.css
@@ -227,4 +227,71 @@ body {
position: absolute;
top: 10px;
right: 10px;
-}
\ No newline at end of file
+}
+
+/* Kanban Board Styles */
+.kanban-board-container {
+ width: 100%;
+ overflow-x: auto;
+ padding: 1rem;
+}
+
+.kanban-board {
+ display: flex;
+ gap: 1rem;
+ min-width: max-content; /* Ensure board expands horizontally */
+}
+
+.kanban-column {
+ flex: 1 1 300px; /* Flex-grow, flex-shrink, and basis */
+ min-width: 300px;
+ max-width: 320px;
+ background-color: #f0f2f5;
+ border-radius: 0.5rem;
+ display: flex;
+ flex-direction: column;
+ max-height: calc(100vh - 250px); /* Adjust based on your layout */
+}
+
+.kanban-column h2 {
+ position: sticky;
+ top: 0;
+ z-index: 2;
+}
+
+.kanban-cards {
+ overflow-y: auto;
+ flex-grow: 1;
+ min-height: 150px; /* Ensure drop zone is available even when empty */
+}
+
+.kanban-card {
+ cursor: grab;
+ transition: background-color 0.2s, box-shadow 0.2s;
+}
+
+.kanban-card:hover {
+ background-color: #f8f9fa;
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+}
+
+.kanban-card .btn-close {
+ transition: opacity 0.2s;
+ opacity: 0;
+}
+
+.kanban-card:hover .btn-close {
+ opacity: 1;
+}
+
+/* For the drag-and-drop placeholder */
+.sortable-ghost {
+ background-color: #e9ecef;
+ border: 2px dashed #ced4da;
+}
+
+.sortable-drag {
+ opacity: 1 !important; /* Override Sortable.js default opacity */
+ box-shadow: 0 8px 16px rgba(0,0,0,0.2);
+ transform: rotate(3deg);
+}
diff --git a/staticfiles/css/custom.css.bak b/staticfiles/css/custom.css.bak
new file mode 100644
index 0000000..551c68f
--- /dev/null
+++ b/staticfiles/css/custom.css.bak
@@ -0,0 +1,230 @@
+/* General App Body & Layout */
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ background-color: #f8f9fa;
+ color: #212529;
+}
+
+/* Main Chat Layout */
+.chat-container {
+ display: flex;
+ height: calc(100vh - 120px); /* Adjusted for header/footer */
+ width: 100%;
+ background-color: #fff;
+ border: 1px solid #dee2e6;
+ border-radius: 0.5rem;
+}
+
+/* Sidebar Styles */
+.chat-sidebar {
+ width: 280px;
+ background-color: #f8f9fa;
+ border-right: 1px solid #dee2e6;
+ display: flex;
+ flex-direction: column;
+ padding: 1rem;
+ flex-shrink: 0;
+}
+
+.new-chat-form {
+ display: flex;
+ margin-bottom: 1rem;
+}
+
+.new-chat-form input {
+ flex: 1;
+ padding: 0.75rem;
+ border: 1px solid #ced4da;
+ border-radius: 0.375rem 0 0 0.375rem;
+ font-size: 0.9rem;
+}
+
+.new-chat-form button {
+ padding: 0.75rem 1rem;
+ background-color: #0d6efd;
+ color: #fff;
+ border: 1px solid #0d6efd;
+ border-radius: 0 0.375rem 0.375rem 0;
+ cursor: pointer;
+}
+
+.conversation-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ overflow-y: auto;
+}
+
+.conversation-list a {
+ display: block;
+ padding: 0.75rem 1rem;
+ color: #495057;
+ text-decoration: none;
+ border-radius: 0.375rem;
+ margin-bottom: 0.25rem;
+}
+
+.conversation-list a:hover {
+ background-color: #e9ecef;
+}
+
+.conversation-list a.active {
+ background-color: #0d6efd;
+ color: #fff;
+}
+
+/* Main Content Area */
+.chat-main {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ background-color: #ffffff;
+ position: relative; /* Needed for loader overlay */
+}
+
+/* Chat Header */
+.chat-header {
+ padding: 1rem 1.5rem;
+ border-bottom: 1px solid #dee2e6;
+ flex-shrink: 0;
+}
+
+.chat-header h3 {
+ margin: 0;
+ font-size: 1.1rem;
+}
+
+/* Message Area */
+.chat-messages {
+ flex: 1;
+ padding: 1.5rem;
+ overflow-y: auto;
+}
+
+.message {
+ margin-bottom: 1rem;
+ display: flex;
+}
+
+.message-content {
+ max-width: 80%;
+ padding: 0.75rem 1rem;
+ border-radius: 0.5rem;
+ line-height: 1.5;
+}
+
+.message.user {
+ justify-content: flex-end;
+}
+
+.message.user .message-content {
+ background-color: #0d6efd;
+ color: #fff;
+}
+
+.message.ai .message-content {
+ background-color: #e9ecef;
+ color: #343a40;
+}
+
+.message-author {
+ font-weight: bold;
+ font-size: 0.8rem;
+ margin-bottom: 0.25rem;
+}
+
+/* Message Input Form */
+.chat-form-container {
+ padding: 1rem 1.5rem;
+ border-top: 1px solid #dee2e6;
+ background-color: #f8f9fa;
+}
+
+.chat-form {
+ display: flex;
+}
+
+.chat-form textarea {
+ flex: 1;
+ padding: 0.75rem;
+ border: 1px solid #ced4da;
+ border-radius: 0.375rem;
+ font-size: 1rem;
+ resize: none;
+}
+
+.chat-form button {
+ margin-left: 1rem;
+ padding: 0.75rem 1.5rem;
+ background-color: #0d6efd;
+ color: #fff;
+ border: none;
+ border-radius: 0.375rem;
+ cursor: pointer;
+}
+
+/* Empty State for Chat */
+.no-conversation-selected {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ color: #6c757d;
+}
+
+/* Loader Styles */
+.loader-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(255, 255, 255, 0.8);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 10;
+}
+
+.loader {
+ border: 5px solid #f3f3f3; /* Light grey */
+ border-top: 5px solid #0d6efd; /* Blue */
+ border-radius: 50%;
+ width: 50px;
+ height: 50px;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* System and AI Command Messages */
+.message.system .message-content {
+ background-color: #f8d7da;
+ color: #721c24;
+ border: 1px solid #f5c6cb;
+}
+
+.message.ai_command .message-content {
+ background-color: #d1ecf1;
+ color: #0c5460;
+ border: 1px solid #bee5eb;
+}
+
+.message.ai_command .message-content pre {
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ margin: 0;
+}
+
+.kanban-card .card-body {
+ position: relative;
+}
+
+.delete-task-form {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+}
\ No newline at end of file