basic chat
This commit is contained in:
parent
330b813563
commit
864ef909c8
BIN
ai/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
ai/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
ai/__pycache__/local_ai_api.cpython-311.pyc
Normal file
BIN
ai/__pycache__/local_ai_api.cpython-311.pyc
Normal file
Binary file not shown.
BIN
assets/pasted-20251119-223636-11a12b2c.png
Normal file
BIN
assets/pasted-20251119-223636-11a12b2c.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
BIN
assets/pasted-20251119-224750-5fe94e0e.png
Normal file
BIN
assets/pasted-20251119-224750-5fe94e0e.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
32
core/migrations/0004_conversation_message.py
Normal file
32
core/migrations/0004_conversation_message.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-11-19 22:34
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0003_todoitem_description_todoitem_tags'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Conversation',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=200)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Message',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('content', models.TextField()),
|
||||||
|
('is_from_user', models.BooleanField(default=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('conversation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='core.conversation')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
Binary file not shown.
@ -24,3 +24,19 @@ class TodoItem(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
class Conversation(models.Model):
|
||||||
|
title = models.CharField(max_length=200)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
class Message(models.Model):
|
||||||
|
conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE, related_name='messages')
|
||||||
|
content = models.TextField()
|
||||||
|
is_from_user = models.BooleanField(default=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Message from {'User' if self.is_from_user else 'AI'} at {self.created_at}"
|
||||||
@ -15,17 +15,20 @@
|
|||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="{% url 'index' %}">AI Task Manager</a>
|
<a class="navbar-brand" href="{% url 'core:index' %}">AI Task Manager</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'index' %}">Table View</a>
|
<a class="nav-link" href="{% url 'core:index' %}">Table View</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'kanban_board' %}">Kanban Board</a>
|
<a class="nav-link" href="{% url 'core:kanban' %}">Kanban Board</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'core:chat' %}">Chat</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
19
core/templates/base_chat.html
Normal file
19
core/templates/base_chat.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{% block title %}Gemini Chat{% endblock %}</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Poppins:wght@600&display=swap" rel="stylesheet">
|
||||||
|
{% load static %}
|
||||||
|
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ timestamp }}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
82
core/templates/core/chat.html
Normal file
82
core/templates/core/chat.html
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
|
||||||
|
{% extends "base_chat.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block title %}Chat{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="chat-container">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside class="chat-sidebar">
|
||||||
|
<form method="post" action="{% url 'core:chat' %}" class="new-chat-form">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="text" name="title" placeholder="New conversation title" required>
|
||||||
|
<button type="submit" class="new-chat-btn">+ New Chat</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<ul class="conversation-list">
|
||||||
|
{% for conv in conversation_list %}
|
||||||
|
<a href="{% url 'core:chat_detail' conv.id %}" class="{% if selected_conversation.id == conv.id %}active{% endif %}">
|
||||||
|
{{ conv.title }}
|
||||||
|
</a>
|
||||||
|
{% empty %}
|
||||||
|
<li>No conversations yet.</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Chat Area -->
|
||||||
|
<main class="chat-main">
|
||||||
|
{% if selected_conversation %}
|
||||||
|
<header class="chat-header">
|
||||||
|
<h3>{{ selected_conversation.title }}</h3>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="chat-messages" id="chat-messages">
|
||||||
|
{% for message in selected_conversation.messages.all %}
|
||||||
|
<div class="message {{ message.sender_type }}">
|
||||||
|
<div class="message-content">
|
||||||
|
<div class="message-author">{% if message.sender_type == 'user' %}You{% else %}AI{% endif %}</div>
|
||||||
|
<p>{{ message.text|linebreaksbr }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chat-form-container">
|
||||||
|
<form method="post" action="{% url 'core:chat_detail' selected_conversation.id %}" class="chat-form">
|
||||||
|
{% csrf_token %}
|
||||||
|
<textarea name="text" placeholder="Send a message..." rows="1"></textarea>
|
||||||
|
<button type="submit">Send</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<div class="no-conversation-selected">
|
||||||
|
<div>
|
||||||
|
<h2>Gemini Chat</h2>
|
||||||
|
<p>Select a conversation or start a new one.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Auto-scroll to the latest message
|
||||||
|
const messagesContainer = document.getElementById('chat-messages');
|
||||||
|
if (messagesContainer) {
|
||||||
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-resize textarea
|
||||||
|
const textarea = document.querySelector('.chat-form textarea');
|
||||||
|
if (textarea) {
|
||||||
|
textarea.addEventListener('input', () => {
|
||||||
|
textarea.style.height = 'auto';
|
||||||
|
textarea.style.height = (textarea.scrollHeight) + 'px';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
78
core/templates/core/conversation_list.html
Normal file
78
core/templates/core/conversation_list.html
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
{% extends "base_chat.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block title %}Chat{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="chat-container">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside class="chat-sidebar">
|
||||||
|
<a href="{% url 'conversation_list' %}" class="new-chat-btn">+ New Chat</a>
|
||||||
|
|
||||||
|
<ul class="conversation-list">
|
||||||
|
{% for conv in conversation_list %}
|
||||||
|
<a href="{% url 'conversation_detail' conv.id %}" class="{% if selected_conversation.id == conv.id %}active{% endif %}">
|
||||||
|
{{ conv.title }}
|
||||||
|
</a>
|
||||||
|
{% empty %}
|
||||||
|
<li>No conversations yet.</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Chat Area -->
|
||||||
|
<main class="chat-main">
|
||||||
|
{% if selected_conversation %}
|
||||||
|
<header class="chat-header">
|
||||||
|
<h3>{{ selected_conversation.title }}</h3>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="chat-messages" id="chat-messages">
|
||||||
|
{% for message in selected_conversation.messages.all %}
|
||||||
|
<div class="message {{ message.sender_type }}">
|
||||||
|
<div class="message-content">
|
||||||
|
<div class="message-author">{% if message.sender_type == 'user' %}You{% else %}AI{% endif %}</div>
|
||||||
|
<p>{{ message.text }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chat-form-container">
|
||||||
|
<form method="post" action="{% url 'conversation_detail' selected_conversation.id %}" class="chat-form">
|
||||||
|
{% csrf_token %}
|
||||||
|
<textarea name="text" placeholder="Send a message..." rows="1"></textarea>
|
||||||
|
<button type="submit">Send</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<div class="no-conversation-selected">
|
||||||
|
<div>
|
||||||
|
<h2>Gemini Chat</h2>
|
||||||
|
<p>Select a conversation or start a new one.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Auto-scroll to the latest message
|
||||||
|
const messagesContainer = document.getElementById('chat-messages');
|
||||||
|
if (messagesContainer) {
|
||||||
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-resize textarea
|
||||||
|
const textarea = document.querySelector('.chat-form textarea');
|
||||||
|
if (textarea) {
|
||||||
|
textarea.addEventListener('input', () => {
|
||||||
|
textarea.style.height = 'auto';
|
||||||
|
textarea.style.height = (textarea.scrollHeight) + 'px';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
15
core/urls.py
15
core/urls.py
@ -1,10 +1,13 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
from .views import index, article_detail, kanban_board, update_task_status
|
app_name = 'core'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", index, name="index"),
|
path("", views.index, name="index"),
|
||||||
path("kanban/", kanban_board, name="kanban_board"),
|
path('kanban/', views.kanban_board, name='kanban'),
|
||||||
path("article/<int:article_id>/", article_detail, name="article_detail"),
|
path('article/<int:article_id>/', views.article_detail, name='article_detail'),
|
||||||
path('update_task_status/', update_task_status, name='update_task_status'),
|
path('update_task_status/', views.update_task_status, name='update_task_status'),
|
||||||
]
|
path('chat/', views.chat_view, name='chat'),
|
||||||
|
path('chat/<int:conversation_id>/', views.chat_view, name='chat_detail'),
|
||||||
|
]
|
||||||
@ -2,9 +2,10 @@ from django.shortcuts import render, redirect, get_object_or_404
|
|||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
import json
|
import json
|
||||||
from .models import Article, TodoItem
|
from .models import Article, TodoItem, Conversation, Message
|
||||||
from .forms import TodoItemForm
|
from .forms import TodoItemForm
|
||||||
import time
|
import time
|
||||||
|
from ai.local_ai_api import LocalAIApi
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@ -57,4 +58,45 @@ def update_task_status(request):
|
|||||||
|
|
||||||
return JsonResponse({'success': True})
|
return JsonResponse({'success': True})
|
||||||
except (json.JSONDecodeError, TypeError, ValueError) as e:
|
except (json.JSONDecodeError, TypeError, ValueError) as e:
|
||||||
return JsonResponse({'success': False, 'error': str(e)}, status=400)
|
return JsonResponse({'success': False, 'error': str(e)}, status=400)
|
||||||
|
|
||||||
|
def chat_view(request, conversation_id=None):
|
||||||
|
if request.method == 'POST':
|
||||||
|
if 'title' in request.POST:
|
||||||
|
title = request.POST.get('title', 'New Conversation')
|
||||||
|
conversation = Conversation.objects.create(title=title)
|
||||||
|
return redirect('core:chat_detail', conversation_id=conversation.id)
|
||||||
|
|
||||||
|
elif 'text' in request.POST and conversation_id:
|
||||||
|
text = request.POST.get('text')
|
||||||
|
selected_conversation = get_object_or_404(Conversation, id=conversation_id)
|
||||||
|
if text:
|
||||||
|
Message.objects.create(conversation=selected_conversation, text=text, sender_type='user')
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = LocalAIApi.create_response({
|
||||||
|
"input": [
|
||||||
|
{"role": "system", "content": "You are a helpful assistant."},
|
||||||
|
{"role": "user", "content": text}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
ai_text = LocalAIApi.extract_text(response)
|
||||||
|
if not ai_text:
|
||||||
|
ai_text = "I couldn't process that. Please try again."
|
||||||
|
except Exception as e:
|
||||||
|
ai_text = f"An error occurred: {str(e)}"
|
||||||
|
|
||||||
|
Message.objects.create(conversation=selected_conversation, text=ai_text, sender_type='ai')
|
||||||
|
|
||||||
|
return redirect('core:chat_detail', conversation_id=selected_conversation.id)
|
||||||
|
|
||||||
|
conversations = Conversation.objects.order_by('-created_at')
|
||||||
|
selected_conversation = None
|
||||||
|
if conversation_id:
|
||||||
|
selected_conversation = get_object_or_404(Conversation, id=conversation_id)
|
||||||
|
|
||||||
|
return render(request, 'core/chat.html', {
|
||||||
|
'conversation_list': conversations,
|
||||||
|
'selected_conversation': selected_conversation,
|
||||||
|
'timestamp': int(time.time()),
|
||||||
|
})
|
||||||
|
|||||||
@ -1,182 +1,230 @@
|
|||||||
/* custom.css */
|
/* General Body Styles */
|
||||||
|
|
||||||
:root {
|
|
||||||
--primary-color: #1A202C;
|
|
||||||
--secondary-color: #F7FAFC;
|
|
||||||
--accent-color: #4299E1;
|
|
||||||
--font-family-headings: 'Poppins', sans-serif;
|
|
||||||
--font-family-body: 'Inter', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: var(--font-family-body);
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
background: linear-gradient(120deg, #fdfbfb 0%, #ebedee 100%);
|
background-color: #f8f9fa;
|
||||||
color: #333;
|
color: #212529;
|
||||||
}
|
margin: 0;
|
||||||
|
height: 100vh;
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-family: var(--font-family-headings);
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section .display-4 {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section .lead {
|
|
||||||
color: #555;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background-color: var(--accent-color);
|
|
||||||
border-color: var(--accent-color);
|
|
||||||
font-weight: 600;
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
background-color: #3182ce; /* A slightly darker shade of accent */
|
|
||||||
border-color: #2c73b9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
border-bottom: 1px solid #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:focus {
|
|
||||||
border-color: var(--accent-color);
|
|
||||||
box-shadow: 0 0 0 0.25rem rgba(66, 153, 225, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table {
|
|
||||||
font-size: 0.95rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #4a5568;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
border-bottom-width: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
padding: 0.4em 0.7em;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-todo {
|
|
||||||
background-color: #e2e8f0;
|
|
||||||
color: #4a5568;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-inprogress {
|
|
||||||
background-color: #bee3f8;
|
|
||||||
color: #2c5282;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-blocked {
|
|
||||||
background-color: #fed7d7;
|
|
||||||
color: #9b2c2c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-done {
|
|
||||||
background-color: #c6f6d5;
|
|
||||||
color: #2f855a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Kanban Board Styles */
|
|
||||||
.kanban-board-container {
|
|
||||||
overflow-x: auto;
|
|
||||||
padding: 1.5rem;
|
|
||||||
background-color: #e9ecef; /* Light grey background for the container */
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-board {
|
|
||||||
display: grid;
|
|
||||||
grid-auto-flow: column;
|
|
||||||
grid-auto-columns: 280px; /* Fixed width for each column */
|
|
||||||
gap: 1.5rem;
|
|
||||||
padding-bottom: 1rem; /* For scrollbar spacing */
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-column {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 280px;
|
|
||||||
max-width: 300px;
|
|
||||||
background-color: #f7fafc;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kanban-column .h5 {
|
/* Main Chat Layout */
|
||||||
font-weight: 600;
|
.chat-container {
|
||||||
}
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
.kanban-cards {
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: 60vh; /* Adjust as needed */
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-card {
|
|
||||||
cursor: grab;
|
|
||||||
transition: box-shadow 0.2s ease-in-out, transform 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-card:hover {
|
|
||||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
||||||
transform: translateY(-3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-card .card-title {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #2d3748;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-card .tags {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader-overlay {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
overflow: hidden;
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background-color: #fff;
|
||||||
z-index: 1000;
|
}
|
||||||
|
|
||||||
|
/* Sidebar Styles */
|
||||||
|
.chat-sidebar {
|
||||||
|
width: 280px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-right: 1px solid #dee2e6;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-chat-form {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-chat-form input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #80bdff;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-chat-form button {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #007bff;
|
||||||
|
border-radius: 0 0.375rem 0.375rem 0;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 1;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-chat-form button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-list a {
|
||||||
|
display: block;
|
||||||
|
padding: 0.85rem 1.25rem;
|
||||||
|
color: #495057;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
transition: background-color 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-list a:hover {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-list a.active {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main Content Area */
|
||||||
|
.chat-main {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat Header */
|
||||||
|
.chat-header {
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: #343a40;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message Area */
|
||||||
|
.chat-messages {
|
||||||
|
flex: 1;
|
||||||
|
padding: 2rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content {
|
||||||
|
max-width: 75%;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.user {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.user .message-content {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 0.75rem 0.75rem 0 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.ai .message-content {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
color: #343a40;
|
||||||
|
border-radius: 0.75rem 0.75rem 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-author {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.user .message-author {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message Input Form */
|
||||||
|
.chat-form-container {
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
border-top: 1px solid #dee2e6;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form textarea {
|
||||||
|
flex: 1;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #495057;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
resize: none;
|
||||||
|
min-height: 50px;
|
||||||
|
max-height: 200px;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #80bdff;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form button {
|
||||||
|
margin-left: 1rem;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty State for Chat */
|
||||||
|
.no-conversation-selected {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
color: #6c757d;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loader {
|
.no-conversation-selected h2 {
|
||||||
border: 8px solid #f3f3f3;
|
font-size: 2rem;
|
||||||
border-top: 8px solid #3498db;
|
margin-bottom: 0.5rem;
|
||||||
border-radius: 50%;
|
color: #343a40;
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
animation: spin 2s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,182 +1,230 @@
|
|||||||
/* custom.css */
|
/* General Body Styles */
|
||||||
|
|
||||||
:root {
|
|
||||||
--primary-color: #1A202C;
|
|
||||||
--secondary-color: #F7FAFC;
|
|
||||||
--accent-color: #4299E1;
|
|
||||||
--font-family-headings: 'Poppins', sans-serif;
|
|
||||||
--font-family-body: 'Inter', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: var(--font-family-body);
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
background: linear-gradient(120deg, #fdfbfb 0%, #ebedee 100%);
|
background-color: #f8f9fa;
|
||||||
color: #333;
|
color: #212529;
|
||||||
}
|
margin: 0;
|
||||||
|
height: 100vh;
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-family: var(--font-family-headings);
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section .display-4 {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section .lead {
|
|
||||||
color: #555;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background-color: var(--accent-color);
|
|
||||||
border-color: var(--accent-color);
|
|
||||||
font-weight: 600;
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
background-color: #3182ce; /* A slightly darker shade of accent */
|
|
||||||
border-color: #2c73b9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
border-bottom: 1px solid #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:focus {
|
|
||||||
border-color: var(--accent-color);
|
|
||||||
box-shadow: 0 0 0 0.25rem rgba(66, 153, 225, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table {
|
|
||||||
font-size: 0.95rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #4a5568;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
border-bottom-width: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
padding: 0.4em 0.7em;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-todo {
|
|
||||||
background-color: #e2e8f0;
|
|
||||||
color: #4a5568;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-inprogress {
|
|
||||||
background-color: #bee3f8;
|
|
||||||
color: #2c5282;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-blocked {
|
|
||||||
background-color: #fed7d7;
|
|
||||||
color: #9b2c2c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-done {
|
|
||||||
background-color: #c6f6d5;
|
|
||||||
color: #2f855a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Kanban Board Styles */
|
|
||||||
.kanban-board-container {
|
|
||||||
overflow-x: auto;
|
|
||||||
padding: 1.5rem;
|
|
||||||
background-color: #e9ecef; /* Light grey background for the container */
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-board {
|
|
||||||
display: grid;
|
|
||||||
grid-auto-flow: column;
|
|
||||||
grid-auto-columns: 280px; /* Fixed width for each column */
|
|
||||||
gap: 1.5rem;
|
|
||||||
padding-bottom: 1rem; /* For scrollbar spacing */
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-column {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 280px;
|
|
||||||
max-width: 300px;
|
|
||||||
background-color: #f7fafc;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kanban-column .h5 {
|
/* Main Chat Layout */
|
||||||
font-weight: 600;
|
.chat-container {
|
||||||
}
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
.kanban-cards {
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: 60vh; /* Adjust as needed */
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-card {
|
|
||||||
cursor: grab;
|
|
||||||
transition: box-shadow 0.2s ease-in-out, transform 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-card:hover {
|
|
||||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
||||||
transform: translateY(-3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-card .card-title {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #2d3748;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban-card .tags {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader-overlay {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
overflow: hidden;
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background-color: #fff;
|
||||||
z-index: 1000;
|
}
|
||||||
|
|
||||||
|
/* Sidebar Styles */
|
||||||
|
.chat-sidebar {
|
||||||
|
width: 280px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-right: 1px solid #dee2e6;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-chat-form {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-chat-form input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #80bdff;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-chat-form button {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #007bff;
|
||||||
|
border-radius: 0 0.375rem 0.375rem 0;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 1;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-chat-form button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-list a {
|
||||||
|
display: block;
|
||||||
|
padding: 0.85rem 1.25rem;
|
||||||
|
color: #495057;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
transition: background-color 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-list a:hover {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-list a.active {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main Content Area */
|
||||||
|
.chat-main {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat Header */
|
||||||
|
.chat-header {
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: #343a40;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message Area */
|
||||||
|
.chat-messages {
|
||||||
|
flex: 1;
|
||||||
|
padding: 2rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content {
|
||||||
|
max-width: 75%;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.user {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.user .message-content {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 0.75rem 0.75rem 0 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.ai .message-content {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
color: #343a40;
|
||||||
|
border-radius: 0.75rem 0.75rem 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-author {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.user .message-author {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message Input Form */
|
||||||
|
.chat-form-container {
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
border-top: 1px solid #dee2e6;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form textarea {
|
||||||
|
flex: 1;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #495057;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
resize: none;
|
||||||
|
min-height: 50px;
|
||||||
|
max-height: 200px;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #80bdff;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form button {
|
||||||
|
margin-left: 1rem;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty State for Chat */
|
||||||
|
.no-conversation-selected {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
color: #6c757d;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loader {
|
.no-conversation-selected h2 {
|
||||||
border: 8px solid #f3f3f3;
|
font-size: 2rem;
|
||||||
border-top: 8px solid #3498db;
|
margin-bottom: 0.5rem;
|
||||||
border-radius: 50%;
|
color: #343a40;
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
animation: spin 2s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user