mirror of
https://github.com/OHV-IT/collabrix.git
synced 2025-12-15 16:48:36 +01:00
- Implement unread message indicators with Material-UI icons - Add BlinkingEnvelope component with theme-compatible colors - Create UnreadMessagesContext for managing unread states - Integrate WebSocket message handling for real-time notifications - Icons only appear for inactive channels/DMs, disappear when opened - Add test functionality (double-click to mark as unread) - Fix WebSocket URL handling for production deployment - Unify WebSocket architecture using presence connection for all messages
398 lines
7.9 KiB
Python
398 lines
7.9 KiB
Python
from pydantic import BaseModel, EmailStr
|
|
from typing import Optional, List
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
|
|
|
|
class UserRole(str, Enum):
|
|
USER = "user"
|
|
ADMIN = "admin"
|
|
SUPERADMIN = "superadmin"
|
|
|
|
|
|
# User Schemas
|
|
class UserBase(BaseModel):
|
|
username: str
|
|
email: EmailStr
|
|
full_name: Optional[str] = None
|
|
profile_picture: Optional[str] = None
|
|
theme: str = "light"
|
|
|
|
|
|
class UserCreate(UserBase):
|
|
password: str
|
|
|
|
|
|
class UserUpdate(BaseModel):
|
|
email: Optional[EmailStr] = None
|
|
full_name: Optional[str] = None
|
|
password: Optional[str] = None
|
|
theme: Optional[str] = None
|
|
|
|
|
|
class UserLogin(BaseModel):
|
|
username: str
|
|
password: str
|
|
|
|
|
|
class UserResponse(UserBase):
|
|
id: int
|
|
is_active: bool
|
|
role: UserRole = UserRole.USER
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class UserWithDepartments(UserResponse):
|
|
departments: List["DepartmentResponse"] = []
|
|
|
|
|
|
# Token Schemas
|
|
class Token(BaseModel):
|
|
access_token: str
|
|
token_type: str
|
|
|
|
|
|
class TokenData(BaseModel):
|
|
username: Optional[str] = None
|
|
|
|
|
|
# Department Schemas
|
|
class DepartmentBase(BaseModel):
|
|
name: str
|
|
description: Optional[str] = None
|
|
|
|
|
|
class DepartmentCreate(DepartmentBase):
|
|
pass
|
|
|
|
|
|
class DepartmentResponse(DepartmentBase):
|
|
id: int
|
|
snippets_enabled: bool
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# Channel Schemas
|
|
class ChannelBase(BaseModel):
|
|
name: str
|
|
description: Optional[str] = None
|
|
department_id: int
|
|
|
|
|
|
class ChannelCreate(ChannelBase):
|
|
pass
|
|
|
|
|
|
class ChannelResponse(ChannelBase):
|
|
id: int
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# Message Schemas
|
|
class MessageBase(BaseModel):
|
|
content: str
|
|
channel_id: int
|
|
snippet_id: Optional[int] = None
|
|
reply_to_id: Optional[int] = None
|
|
|
|
|
|
class MessageCreate(MessageBase):
|
|
pass
|
|
|
|
|
|
class MessageResponse(MessageBase):
|
|
id: int
|
|
sender_id: int
|
|
created_at: datetime
|
|
is_deleted: bool = False
|
|
sender_username: Optional[str] = None
|
|
sender_full_name: Optional[str] = None
|
|
sender_profile_picture: Optional[str] = None
|
|
attachments: List["FileAttachmentResponse"] = []
|
|
snippet: Optional["SnippetResponse"] = None
|
|
reply_to: Optional[dict] = None # Contains replied message info
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# Direct Message Schemas
|
|
class DirectMessageBase(BaseModel):
|
|
content: str
|
|
receiver_id: int
|
|
snippet_id: Optional[int] = None
|
|
reply_to_id: Optional[int] = None
|
|
|
|
|
|
class DirectMessageCreate(DirectMessageBase):
|
|
pass
|
|
|
|
|
|
class DirectMessageResponse(DirectMessageBase):
|
|
id: int
|
|
sender_id: int
|
|
created_at: datetime
|
|
is_read: bool
|
|
sender_username: Optional[str] = None
|
|
receiver_username: Optional[str] = None
|
|
sender_full_name: Optional[str] = None
|
|
sender_profile_picture: Optional[str] = None
|
|
snippet: Optional["SnippetResponse"] = None
|
|
reply_to: Optional[dict] = None # Contains replied message info
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# File Attachment Schemas
|
|
class FileAttachmentCreate(BaseModel):
|
|
permission: str = "read" # "read" or "write"
|
|
|
|
|
|
class FileAttachmentResponse(BaseModel):
|
|
id: int
|
|
filename: str
|
|
original_filename: str
|
|
mime_type: str
|
|
file_size: int
|
|
uploaded_at: datetime
|
|
message_id: int
|
|
upload_permission: Optional[str] = "read"
|
|
uploader_id: Optional[int] = None
|
|
is_editable: bool = False # Computed: whether current user can edit
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# Snippet Schemas
|
|
class SnippetBase(BaseModel):
|
|
title: str
|
|
language: str
|
|
content: str
|
|
tags: Optional[str] = None
|
|
visibility: str = "private" # private, department, organization
|
|
department_id: Optional[int] = None
|
|
|
|
|
|
class SnippetCreate(SnippetBase):
|
|
pass
|
|
|
|
|
|
class SnippetUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
language: Optional[str] = None
|
|
content: Optional[str] = None
|
|
tags: Optional[str] = None
|
|
visibility: Optional[str] = None
|
|
department_id: Optional[int] = None
|
|
|
|
|
|
class SnippetResponse(SnippetBase):
|
|
id: int
|
|
owner_id: int
|
|
owner_username: Optional[str] = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class LanguageBase(BaseModel):
|
|
code: str
|
|
name: str
|
|
|
|
|
|
class LanguageCreate(LanguageBase):
|
|
pass
|
|
|
|
|
|
class LanguageResponse(LanguageBase):
|
|
id: int
|
|
is_default: bool
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class TranslationEntryResponse(BaseModel):
|
|
translation_id: int
|
|
language_id: int
|
|
language_code: str
|
|
language_name: str
|
|
value: str
|
|
|
|
|
|
class TranslationGroupResponse(BaseModel):
|
|
key: str
|
|
label: str
|
|
description: Optional[str] = None
|
|
entries: List[TranslationEntryResponse]
|
|
|
|
|
|
class TranslationUpdateRequest(BaseModel):
|
|
translation_id: int
|
|
value: str
|
|
|
|
|
|
# Kanban Schemas
|
|
class KanbanBoardBase(BaseModel):
|
|
name: str = "Kanban Board"
|
|
|
|
|
|
class KanbanBoardCreate(KanbanBoardBase):
|
|
channel_id: int
|
|
|
|
|
|
class KanbanBoardUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
|
|
|
|
class KanbanBoardResponse(KanbanBoardBase):
|
|
id: int
|
|
channel_id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class KanbanColumnBase(BaseModel):
|
|
name: str
|
|
position: int = 0
|
|
color: Optional[str] = None
|
|
|
|
|
|
class KanbanColumnCreate(KanbanColumnBase):
|
|
board_id: int
|
|
|
|
|
|
class KanbanColumnUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
position: Optional[int] = None
|
|
color: Optional[str] = None
|
|
|
|
|
|
class KanbanColumnResponse(KanbanColumnBase):
|
|
id: int
|
|
board_id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class KanbanCardBase(BaseModel):
|
|
title: str
|
|
description: Optional[str] = None
|
|
assignee_id: Optional[int] = None
|
|
position: int = 0
|
|
due_date: Optional[datetime] = None
|
|
priority: Optional[str] = "medium"
|
|
labels: Optional[str] = None
|
|
|
|
|
|
class KanbanCardCreate(KanbanCardBase):
|
|
column_id: int
|
|
|
|
|
|
class KanbanCardUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
description: Optional[str] = None
|
|
assignee_id: Optional[int] = None
|
|
position: Optional[int] = None
|
|
due_date: Optional[datetime] = None
|
|
priority: Optional[str] = None
|
|
labels: Optional[str] = None
|
|
|
|
|
|
class KanbanCardResponse(KanbanCardBase):
|
|
id: int
|
|
column_id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class KanbanBoardWithColumns(KanbanBoardResponse):
|
|
columns: List["KanbanColumnWithCards"] = []
|
|
|
|
|
|
class KanbanColumnWithCards(KanbanColumnResponse):
|
|
cards: List[KanbanCardResponse] = []
|
|
|
|
|
|
# Checklist Schemas
|
|
class KanbanChecklistBase(BaseModel):
|
|
title: str
|
|
position: int = 0
|
|
|
|
|
|
class KanbanChecklistCreate(KanbanChecklistBase):
|
|
card_id: int
|
|
|
|
|
|
class KanbanChecklistUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
position: Optional[int] = None
|
|
|
|
|
|
class KanbanChecklistResponse(KanbanChecklistBase):
|
|
id: int
|
|
card_id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class KanbanChecklistItemBase(BaseModel):
|
|
title: str
|
|
is_completed: bool = False
|
|
position: int = 0
|
|
|
|
|
|
class KanbanChecklistItemCreate(KanbanChecklistItemBase):
|
|
checklist_id: int
|
|
|
|
|
|
class KanbanChecklistItemUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
is_completed: Optional[bool] = None
|
|
position: Optional[int] = None
|
|
|
|
|
|
class KanbanChecklistItemResponse(KanbanChecklistItemBase):
|
|
id: int
|
|
checklist_id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class KanbanChecklistWithItems(KanbanChecklistResponse):
|
|
items: List[KanbanChecklistItemResponse] = []
|
|
|
|
|
|
class KanbanCardWithChecklists(KanbanCardResponse):
|
|
checklists: List[KanbanChecklistWithItems] = []
|
|
|