use db::project::project_views as view_entity; use db::project::project_views::Column as Vc; use sea_orm::{ ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, Set, }; use time::OffsetDateTime; use uuid::Uuid; use crate::error::{ProjectError, ProjectResult}; use crate::features::lifecycle::usecase::log_activity; use crate::features::ProjectService; use crate::permission; use super::{CreateViewCmd, UpdateViewCmd, ViewPage, ViewView}; pub async fn create_view( svc: &ProjectService, actor: Uuid, space_id: Uuid, project_id: Uuid, cmd: CreateViewCmd, ) -> ProjectResult { crate::features::authz::require( svc, actor, space_id, project_id, permission::MANAGE_VIEWS, ) .await?; let id = Uuid::new_v4(); let now = OffsetDateTime::now_utc(); let row = view_entity::ActiveModel { id: Set(id), project_id: Set(project_id), creator_id: Set(actor), name: Set(cmd.name), description: Set(cmd.description), filter: Set(cmd.filter), sort_by: Set(cmd.sort_by), group_by: Set(cmd.group_by), layout: Set(cmd.layout), is_shared: Set(cmd.is_shared), sort_order: Set(0), created_at: Set(now), updated_at: Set(None), } .insert(&svc.db.writer()) .await?; log_activity(svc, project_id, actor, "view_created", "view", Some(id), None).await; Ok(view_view(&row)) } pub async fn list_views( svc: &ProjectService, actor: Uuid, space_id: Uuid, project_id: Uuid, ) -> ProjectResult { crate::features::authz::require(svc, actor, space_id, project_id, permission::VIEW_PROJECT) .await?; let rows = view_entity::Entity::find() .filter(Vc::ProjectId.eq(project_id)) .order_by_asc(Vc::SortOrder) .order_by_desc(Vc::CreatedAt) .all(&svc.db.reader()) .await?; let items = rows.iter().map(view_view).collect::>(); let total = items.len() as u64; Ok(ViewPage { total, items }) } pub async fn get_view( svc: &ProjectService, actor: Uuid, space_id: Uuid, project_id: Uuid, view_id: Uuid, ) -> ProjectResult { crate::features::authz::require(svc, actor, space_id, project_id, permission::VIEW_PROJECT) .await?; let row = view_entity::Entity::find_by_id(view_id) .filter(Vc::ProjectId.eq(project_id)) .one(&svc.db.reader()) .await? .ok_or_else(|| ProjectError::NotFound(format!("view {view_id} in project {project_id}")))?; Ok(view_view(&row)) } pub async fn update_view( svc: &ProjectService, actor: Uuid, space_id: Uuid, project_id: Uuid, view_id: Uuid, cmd: UpdateViewCmd, ) -> ProjectResult { crate::features::authz::require( svc, actor, space_id, project_id, permission::MANAGE_VIEWS, ) .await?; let current = view_entity::Entity::find_by_id(view_id) .filter(Vc::ProjectId.eq(project_id)) .one(&svc.db.reader()) .await? .ok_or_else(|| ProjectError::NotFound(format!("view {view_id} in project {project_id}")))?; let mut active: view_entity::ActiveModel = current.into(); if let Some(name) = cmd.name { active.name = Set(name); } if cmd.description.is_some() { active.description = Set(cmd.description); } if let Some(filter) = cmd.filter { active.filter = Set(filter); } if let Some(sort_by) = cmd.sort_by { active.sort_by = Set(sort_by); } if cmd.group_by.is_some() { active.group_by = Set(cmd.group_by); } if let Some(layout) = cmd.layout { active.layout = Set(layout); } if let Some(shared) = cmd.is_shared { active.is_shared = Set(shared); } active.updated_at = Set(Some(OffsetDateTime::now_utc())); let updated = active.update(&svc.db.writer()).await?; log_activity(svc, project_id, actor, "view_updated", "view", Some(view_id), None).await; Ok(view_view(&updated)) } pub async fn delete_view( svc: &ProjectService, actor: Uuid, space_id: Uuid, project_id: Uuid, view_id: Uuid, ) -> ProjectResult<()> { crate::features::authz::require( svc, actor, space_id, project_id, permission::MANAGE_VIEWS, ) .await?; let deleted = view_entity::Entity::delete_many() .filter(Vc::Id.eq(view_id)) .filter(Vc::ProjectId.eq(project_id)) .exec(&svc.db.writer()) .await?; if deleted.rows_affected == 0 { return Err(ProjectError::NotFound(format!( "view {view_id} in project {project_id}" ))); } log_activity(svc, project_id, actor, "view_deleted", "view", Some(view_id), None).await; Ok(()) } fn view_view(m: &view_entity::Model) -> ViewView { ViewView { id: m.id, project_id: m.project_id, creator_id: m.creator_id, name: m.name.clone(), description: m.description.clone(), filter: m.filter.clone(), sort_by: m.sort_by.clone(), group_by: m.group_by.clone(), layout: m.layout.clone(), is_shared: m.is_shared, sort_order: m.sort_order, created_at: m.created_at, updated_at: m.updated_at, } }