mirror of
https://github.com/OHV-IT/collabrix.git
synced 2025-12-15 16:48:36 +01:00
- Added complete Kanban board functionality with drag-and-drop - Implemented auto-save for Kanban card editing (no more edit button) - Added route persistence to remember last visited page on reload - Improved Kanban UI design with slimmer borders and compact layout - Added checklist functionality for Kanban cards - Enhanced file upload and direct messaging features - Improved authentication and user management - Added toast notifications system - Various UI/UX improvements and bug fixes
285 lines
12 KiB
Python
285 lines
12 KiB
Python
from typing import Optional, List
|
|
from sqlmodel import SQLModel, Field, Relationship
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
|
|
|
|
class SnippetVisibility(str, Enum):
|
|
PRIVATE = "private"
|
|
DEPARTMENT = "department"
|
|
ORGANIZATION = "organization"
|
|
|
|
|
|
class Language(SQLModel, table=True):
|
|
__tablename__ = "language"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
code: str = Field(unique=True, index=True)
|
|
name: str
|
|
is_default: bool = Field(default=False)
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
translations: List["Translation"] = Relationship(back_populates="language")
|
|
|
|
|
|
class Translation(SQLModel, table=True):
|
|
__tablename__ = "translation"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
key: str = Field(index=True)
|
|
value: str = Field(default="")
|
|
language_id: int = Field(foreign_key="language.id")
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
language: Language = Relationship(back_populates="translations")
|
|
|
|
|
|
# Association table for User-Department many-to-many relationship
|
|
class UserDepartmentLink(SQLModel, table=True):
|
|
__tablename__ = "user_department"
|
|
|
|
user_id: Optional[int] = Field(default=None, foreign_key="user.id", primary_key=True)
|
|
department_id: Optional[int] = Field(default=None, foreign_key="department.id", primary_key=True)
|
|
|
|
|
|
# Association table for Snippet-Department access control
|
|
class SnippetDepartmentLink(SQLModel, table=True):
|
|
__tablename__ = "snippet_department"
|
|
|
|
snippet_id: Optional[int] = Field(default=None, foreign_key="snippet.id", primary_key=True)
|
|
department_id: Optional[int] = Field(default=None, foreign_key="department.id", primary_key=True)
|
|
enabled: bool = Field(default=True) # Can be toggled by admins
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
|
|
class User(SQLModel, table=True):
|
|
__tablename__ = "user"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
username: str = Field(unique=True, index=True)
|
|
email: str = Field(unique=True, index=True)
|
|
hashed_password: str
|
|
full_name: Optional[str] = None
|
|
profile_picture: Optional[str] = None
|
|
theme: str = Field(default="light") # 'light' or 'dark'
|
|
is_active: bool = Field(default=True)
|
|
is_admin: bool = Field(default=False)
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
departments: List["Department"] = Relationship(back_populates="users", link_model=UserDepartmentLink)
|
|
messages: List["Message"] = Relationship(back_populates="sender")
|
|
snippets: List["Snippet"] = Relationship(back_populates="owner")
|
|
sent_direct_messages: List["DirectMessage"] = Relationship(back_populates="sender", sa_relationship_kwargs={"foreign_keys": "DirectMessage.sender_id"})
|
|
received_direct_messages: List["DirectMessage"] = Relationship(back_populates="receiver", sa_relationship_kwargs={"foreign_keys": "DirectMessage.receiver_id"})
|
|
uploaded_files: List["FileAttachment"] = Relationship(back_populates="uploader")
|
|
file_permissions: List["FilePermission"] = Relationship(back_populates="user")
|
|
|
|
|
|
class Department(SQLModel, table=True):
|
|
__tablename__ = "department"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
name: str = Field(unique=True, index=True)
|
|
description: Optional[str] = None
|
|
snippets_enabled: bool = Field(default=True) # Master switch for snippet access
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
users: List[User] = Relationship(back_populates="departments", link_model=UserDepartmentLink)
|
|
channels: List["Channel"] = Relationship(back_populates="department")
|
|
allowed_snippets: List["Snippet"] = Relationship(link_model=SnippetDepartmentLink)
|
|
|
|
|
|
class Channel(SQLModel, table=True):
|
|
__tablename__ = "channel"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
name: str = Field(index=True)
|
|
description: Optional[str] = None
|
|
department_id: int = Field(foreign_key="department.id")
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
department: Department = Relationship(back_populates="channels")
|
|
messages: List["Message"] = Relationship(back_populates="channel")
|
|
kanban_board: Optional["KanbanBoard"] = Relationship(back_populates="channel")
|
|
|
|
|
|
class Message(SQLModel, table=True):
|
|
__tablename__ = "message"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
content: str
|
|
sender_id: int = Field(foreign_key="user.id")
|
|
channel_id: int = Field(foreign_key="channel.id")
|
|
snippet_id: Optional[int] = Field(default=None, foreign_key="snippet.id")
|
|
reply_to_id: Optional[int] = Field(default=None, foreign_key="message.id") # Reply to another message
|
|
is_deleted: bool = Field(default=False)
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
sender: User = Relationship(back_populates="messages")
|
|
channel: Channel = Relationship(back_populates="messages")
|
|
attachments: List["FileAttachment"] = Relationship(back_populates="message")
|
|
snippet: Optional["Snippet"] = Relationship()
|
|
|
|
|
|
class DirectMessage(SQLModel, table=True):
|
|
__tablename__ = "direct_message"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
content: str
|
|
sender_id: int = Field(foreign_key="user.id")
|
|
receiver_id: int = Field(foreign_key="user.id")
|
|
snippet_id: Optional[int] = Field(default=None, foreign_key="snippet.id")
|
|
reply_to_id: Optional[int] = Field(default=None, foreign_key="direct_message.id") # Reply to another DM
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
is_read: bool = Field(default=False)
|
|
|
|
# Relationships
|
|
sender: User = Relationship(back_populates="sent_direct_messages", sa_relationship_kwargs={"foreign_keys": "DirectMessage.sender_id"})
|
|
receiver: User = Relationship(back_populates="received_direct_messages", sa_relationship_kwargs={"foreign_keys": "DirectMessage.receiver_id"})
|
|
snippet: Optional["Snippet"] = Relationship()
|
|
|
|
|
|
class FileAttachment(SQLModel, table=True):
|
|
__tablename__ = "file_attachment"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
filename: str
|
|
original_filename: str
|
|
mime_type: str
|
|
file_size: int
|
|
file_path: str
|
|
message_id: int = Field(foreign_key="message.id")
|
|
uploader_id: Optional[int] = Field(default=None, foreign_key="user.id")
|
|
webdav_path: Optional[str] = None
|
|
upload_permission: str = Field(default="read") # "read" or "write"
|
|
is_editable: bool = Field(default=False)
|
|
uploaded_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
message: Message = Relationship(back_populates="attachments")
|
|
uploader: Optional[User] = Relationship(back_populates="uploaded_files")
|
|
permissions: List["FilePermission"] = Relationship(back_populates="file")
|
|
|
|
|
|
class FilePermission(SQLModel, table=True):
|
|
__tablename__ = "file_permission"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
file_id: int = Field(foreign_key="file_attachment.id")
|
|
user_id: int = Field(foreign_key="user.id")
|
|
permission: str = Field(default="read") # "read" or "write"
|
|
granted_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
file: FileAttachment = Relationship(back_populates="permissions")
|
|
user: User = Relationship(back_populates="file_permissions")
|
|
|
|
|
|
class Snippet(SQLModel, table=True):
|
|
__tablename__ = "snippet"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
title: str = Field(index=True)
|
|
language: str = Field(index=True)
|
|
content: str
|
|
tags: Optional[str] = None # Comma-separated
|
|
visibility: SnippetVisibility = Field(default=SnippetVisibility.PRIVATE)
|
|
|
|
owner_id: int = Field(foreign_key="user.id")
|
|
department_id: Optional[int] = Field(default=None, foreign_key="department.id")
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
owner: User = Relationship(back_populates="snippets")
|
|
department: Optional[Department] = Relationship()
|
|
allowed_departments: List["Department"] = Relationship(link_model=SnippetDepartmentLink)
|
|
|
|
|
|
# Kanban Board Models
|
|
class KanbanBoard(SQLModel, table=True):
|
|
__tablename__ = "kanban_board"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
channel_id: int = Field(foreign_key="channel.id")
|
|
name: str = Field(default="Kanban Board")
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
channel: Channel = Relationship(back_populates="kanban_board")
|
|
columns: List["KanbanColumn"] = Relationship(back_populates="board")
|
|
|
|
|
|
class KanbanColumn(SQLModel, table=True):
|
|
__tablename__ = "kanban_column"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
board_id: int = Field(foreign_key="kanban_board.id")
|
|
name: str
|
|
position: int = Field(default=0) # For ordering columns
|
|
color: Optional[str] = Field(default=None) # Hex color for the column
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
board: KanbanBoard = Relationship(back_populates="columns")
|
|
cards: List["KanbanCard"] = Relationship(back_populates="column")
|
|
|
|
|
|
class KanbanCard(SQLModel, table=True):
|
|
__tablename__ = "kanban_card"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
column_id: int = Field(foreign_key="kanban_column.id")
|
|
title: str
|
|
description: Optional[str] = Field(default=None)
|
|
assignee_id: Optional[int] = Field(default=None, foreign_key="user.id")
|
|
position: int = Field(default=0) # For ordering cards within a column
|
|
due_date: Optional[datetime] = Field(default=None)
|
|
priority: Optional[str] = Field(default="medium") # low, medium, high
|
|
labels: Optional[str] = Field(default=None) # JSON string for labels/tags
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
column: KanbanColumn = Relationship(back_populates="cards")
|
|
assignee: Optional[User] = Relationship()
|
|
checklists: List["KanbanChecklist"] = Relationship(back_populates="card")
|
|
|
|
|
|
class KanbanChecklist(SQLModel, table=True):
|
|
__tablename__ = "kanban_checklist"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
card_id: int = Field(foreign_key="kanban_card.id")
|
|
title: str
|
|
position: int = Field(default=0) # For ordering checklists within a card
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
card: KanbanCard = Relationship(back_populates="checklists")
|
|
items: List["KanbanChecklistItem"] = Relationship(back_populates="checklist")
|
|
|
|
|
|
class KanbanChecklistItem(SQLModel, table=True):
|
|
__tablename__ = "kanban_checklist_item"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
checklist_id: int = Field(foreign_key="kanban_checklist.id")
|
|
title: str
|
|
is_completed: bool = Field(default=False)
|
|
position: int = Field(default=0) # For ordering items within a checklist
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
# Relationships
|
|
checklist: KanbanChecklist = Relationship(back_populates="items")
|