feat: implement comments and activity panel in workspace
- Added CommentsPanel component for managing comments on documents. - Integrated comment creation, deletion, and resolution functionalities. - Implemented activity tracking for document interactions. - Enhanced SketchPage with editing and saving capabilities. - Introduced template management API endpoints for creating, sharing, and deleting templates. - Added runtime validation report for frontend and backend. - Created GettingStartedPanel for user onboarding with template options. - Implemented Playwright E2E tests for accessibility and export functionality.
This commit is contained in:
parent
6e354ba041
commit
809235e962
41 changed files with 2840 additions and 285 deletions
|
|
@ -30,6 +30,7 @@ def _serialize(doc: dict[str, Any]) -> dict[str, Any]:
|
|||
"title": doc["title"],
|
||||
"template_id": doc.get("template_id"),
|
||||
"stead_id": doc.get("stead_id"),
|
||||
"parent_page_id": doc.get("parent_page_id"),
|
||||
"content": doc.get("content", {}),
|
||||
"created_by": doc["created_by"],
|
||||
"created_at": doc["created_at"],
|
||||
|
|
@ -61,6 +62,10 @@ async def create_document(
|
|||
created_by: str,
|
||||
data: DocumentCreate,
|
||||
) -> dict[str, Any]:
|
||||
if data.parent_page_id:
|
||||
parent = await db[COLLECTION].find_one({"_id": data.parent_page_id, **access_query(owner_id)})
|
||||
if parent is None or parent.get("type") == "stead":
|
||||
raise ValueError("Parent page is not accessible or is not a page")
|
||||
now = datetime.now(UTC)
|
||||
doc_id = str(uuid.uuid4())
|
||||
doc = {
|
||||
|
|
@ -70,6 +75,7 @@ async def create_document(
|
|||
"title": data.title,
|
||||
"template_id": data.template_id,
|
||||
"stead_id": data.stead_id,
|
||||
"parent_page_id": data.parent_page_id,
|
||||
"content": data.content,
|
||||
"created_by": created_by,
|
||||
"created_at": now,
|
||||
|
|
@ -97,6 +103,7 @@ async def list_documents(
|
|||
user_id: int,
|
||||
doc_type: CreateType | None = None,
|
||||
stead_id: str | None = None,
|
||||
parent_page_id: str | None = None,
|
||||
starred: bool | None = None,
|
||||
sort: SortField = "updated_at",
|
||||
) -> list[dict[str, Any]]:
|
||||
|
|
@ -106,6 +113,8 @@ async def list_documents(
|
|||
extras["type"] = doc_type
|
||||
if stead_id:
|
||||
extras["stead_id"] = stead_id
|
||||
if parent_page_id:
|
||||
extras["parent_page_id"] = parent_page_id
|
||||
if starred is not None:
|
||||
extras["starred"] = starred
|
||||
if extras:
|
||||
|
|
@ -156,6 +165,23 @@ async def update_document(
|
|||
return None
|
||||
if not can_edit(existing, user_id):
|
||||
return None
|
||||
if data.content is not None or data.title is not None:
|
||||
from app.services.history_service import create_version
|
||||
|
||||
await create_version(
|
||||
db,
|
||||
existing,
|
||||
user_id,
|
||||
existing.get("created_by", "Collaborator"),
|
||||
"Before update",
|
||||
)
|
||||
if "parent_page_id" in data.model_fields_set:
|
||||
if data.parent_page_id == doc_id:
|
||||
return None
|
||||
if data.parent_page_id is not None:
|
||||
parent = await db[COLLECTION].find_one({"_id": data.parent_page_id, **access_query(user_id)})
|
||||
if parent is None or parent.get("type") == "stead":
|
||||
return None
|
||||
|
||||
updates: dict[str, Any] = {"updated_at": datetime.now(UTC)}
|
||||
if data.title is not None:
|
||||
|
|
@ -164,6 +190,10 @@ async def update_document(
|
|||
updates["content"] = data.content
|
||||
if data.starred is not None:
|
||||
updates["starred"] = data.starred
|
||||
if data.stead_id is not None:
|
||||
updates["stead_id"] = data.stead_id
|
||||
if "parent_page_id" in data.model_fields_set:
|
||||
updates["parent_page_id"] = data.parent_page_id
|
||||
|
||||
result = await db[COLLECTION].find_one_and_update(
|
||||
{"_id": doc_id},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue