Skip to content

Commit

Permalink
feat: page for creating rooms
Browse files Browse the repository at this point in the history
  • Loading branch information
krrish-sehgal committed Jan 25, 2025
1 parent dac9a28 commit 5d0f321
Show file tree
Hide file tree
Showing 10 changed files with 414 additions and 9 deletions.
9 changes: 9 additions & 0 deletions blt/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@
user_sizzle_report,
view_hunt,
weekly_report,
RoomsListView,
RoomCreateView,
RoomDetailView,
join_room,
)
from website.views.project import (
ProjectBadgeView,
Expand Down Expand Up @@ -860,6 +864,11 @@
path("projects/create/", create_project, name="create_project"),
path("project/<slug:slug>/", ProjectsDetailView.as_view(), name="projects_detail"),
path("slack/events", slack_events, name="slack_events"),

path("discussion-rooms/", RoomsListView.as_view(), name="rooms_list"),
path("discussion-rooms/create/", RoomCreateView.as_view(), name="room_create"),
path("discussion-rooms/<int:pk>/", RoomDetailView.as_view(), name="room_detail"),
path("discussion-rooms/join/<int:pk>/", join_room, name="join_room"),
]

if settings.DEBUG:
Expand Down
10 changes: 10 additions & 0 deletions website/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from mdeditor.fields import MDTextFormField

from .models import Bid, IpReport, Monitor, UserProfile
from website.models import Room


class UserProfileForm(forms.ModelForm):
Expand Down Expand Up @@ -111,3 +112,12 @@ class GitHubURLForm(forms.Form):
required=True,
widget=forms.TextInput(attrs={"placeholder": "Add any Github URL"}),
)


class RoomForm(forms.ModelForm):
class Meta:
model = Room
fields = ["name", "type", "custom_type", "description"]
widgets = {
"type": forms.Select(attrs={"onchange": "toggleCustomTypeField(this)"}),
}
95 changes: 95 additions & 0 deletions website/migrations/0183_room_roommessage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Generated by Django 5.1.4 on 2025-01-25 09:26

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("website", "0182_project_status"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name="Room",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=255)),
(
"type",
models.CharField(
choices=[
("project", "Project"),
("bug", "Bug"),
("org", "Organization"),
("custom", "Custom"),
],
max_length=20,
),
),
(
"custom_type",
models.CharField(blank=True, max_length=255, null=True),
),
("description", models.TextField(blank=True, null=True)),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"admin",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="admin_rooms",
to=settings.AUTH_USER_MODEL,
),
),
(
"users",
models.ManyToManyField(
blank=True, related_name="rooms", to=settings.AUTH_USER_MODEL
),
),
],
),
migrations.CreateModel(
name="RoomMessage",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("message", models.TextField()),
("timestamp", models.DateTimeField(auto_now_add=True)),
(
"room",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="messages",
to="website.room",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="messages",
to=settings.AUTH_USER_MODEL,
),
),
],
),
]
30 changes: 30 additions & 0 deletions website/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1298,3 +1298,33 @@ class Meta:

def __str__(self):
return f"{self.contributor.name} in {self.repo.name} " f"on {self.date} [{self.granularity}]"


class Room(models.Model):
ROOM_TYPES = [
("project", "Project"),
("bug", "Bug"),
("org", "Organization"),
("custom", "Custom"),
]

name = models.CharField(max_length=255)
type = models.CharField(max_length=20, choices=ROOM_TYPES)
custom_type = models.CharField(max_length=255, blank=True, null=True)
description = models.TextField(blank=True, null=True)
admin = models.ForeignKey(User, related_name="admin_rooms", on_delete=models.CASCADE)
users = models.ManyToManyField(User, related_name="rooms", blank=True)
created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return self.name


class RoomMessage(models.Model):
room = models.ForeignKey(Room, related_name="messages", on_delete=models.CASCADE)
user = models.ForeignKey(User, related_name="messages", on_delete=models.CASCADE)
message = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)

def __str__(self):
return f"{self.user.username}: {self.message[:50]}"
9 changes: 9 additions & 0 deletions website/templates/includes/sidenav.html
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,15 @@
<span>SimilarityScan</span>
</a>
</li>
<li class="mb-2 {% if request.path == '/discussion-rooms/' %}bg-gray-200{% endif %}">
<a href="{% url 'rooms_list' %}"
class="flex items-center w-full no-underline p-2 {% if request.path == '/discussion-rooms/' %}text-black{% else %}hover:text-red-500 text-black{% endif %}">
<div class="icon text-indigo-500">
<i class="fas fa-door-open fa-lg"></i> <!-- Updated icon -->
</div>
<span>Rooms</span>
</a>
</li>
</ul>
<!-- Spacer -->
<div class="my-4 border-t border-gray-300"></div>
Expand Down
107 changes: 107 additions & 0 deletions website/templates/room_detail.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{% extends "base.html" %}
{% block title %}
{{ room.name }} - Room
{% endblock title %}
{% block content %}
{% include "includes/sidenav.html" %}

<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="flex flex-col lg:flex-row">
<div class="lg:w-3/4">
<h1 class="text-3xl font-bold text-gray-900 mb-8">{{ room.name }}</h1>
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
<p class="text-gray-600">{{ room.description|default:"No description available." }}</p>
</div>
<div id="chat" class="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
<div id="messages" class="overflow-y-auto h-64 mb-4"></div>
<form id="messageForm">
<input type="text" id="messageInput" class="border border-gray-300 rounded-lg w-full p-2" placeholder="Type a message...">
<button type="submit" class="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded mt-2">Send</button>
</form>
</div>
</div>
<div class="lg:w-1/4 lg:pl-8">
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
<h2 class="text-xl font-bold text-gray-900 mb-4">Admin</h2>
<p><a href="/profile/{{ room.admin.username }}" class="text-red-500 hover:underline">{{ room.admin.username }}</a></p>
</div>
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h2 class="text-xl font-bold text-gray-900 mb-4">Members</h2>
<ul id="membersList">
{% for user in room.users.all %}
<li id="user-{{ user.id }}">
<a href="/profile/{{ user.username }}" class="text-gray-700 hover:underline">{{ user.username }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>

<script>
const roomId = "{{ room.id }}";
const userId = "{{ user.id }}";
const messagesDiv = document.getElementById('messages');
const messageForm = document.getElementById('messageForm');
const messageInput = document.getElementById('messageInput');
const membersList = document.getElementById('membersList');

function addMessage(message) {
const messageElement = document.createElement('div');
messageElement.classList.add('mb-2');
messageElement.innerHTML = `<strong>${message.user.username}:</strong> ${message.message}`;
messagesDiv.appendChild(messageElement);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}

function updateMembers(members) {
membersList.innerHTML = '';
members.forEach(member => {
const memberElement = document.createElement('li');
memberElement.id = `user-${member.id}`;
memberElement.innerHTML = `<a href="/profile/${member.username}" class="text-gray-700 hover:underline">${member.username}</a>`;
if (member.is_connected) {
memberElement.classList.add('text-green-500');
}
membersList.appendChild(memberElement);
});
}

messageForm.addEventListener('submit', function(event) {
event.preventDefault();
const message = messageInput.value;
if (message.trim() === '') return;

fetch(`/api/rooms/${roomId}/messages/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({ message, user: userId })
})
.then(response => response.json())
.then(data => {
addMessage(data);
messageInput.value = '';
});
});

// WebSocket connection for real-time updates
const socket = new WebSocket(`ws://${window.location.host}/ws/room/${roomId}/`);

socket.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.type === 'message') {
addMessage(data.message);
} else if (data.type === 'members') {
updateMembers(data.members);
}
};

socket.onclose = function(event) {
console.error('WebSocket closed unexpectedly');
};
</script>
{% endblock content %}
17 changes: 17 additions & 0 deletions website/templates/room_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block title %}
Create Room
{% endblock title %}
{% block content %}
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<h1 class="text-3xl font-bold text-gray-900 mb-8">Create Room</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<div class="mt-4">
<button type="submit" class="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded">Create</button>
<a href="{% url 'rooms_list' %}" class="bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded">Cancel</a>
</div>
</form>
</div>
{% endblock content %}
Loading

0 comments on commit 5d0f321

Please sign in to comment.