//! Saved views commands. //! //! Manages custom filter views that users can save and pin for quick access. //! Views can filter tasks, emails, or events with custom criteria and sort orders. use serde::Deserialize; use std::sync::Arc; use tauri::State; use tracing::instrument; use goingson_core::{NewSavedView, SavedView, SavedViewId, SortDirection, SortField, ViewFilters, ViewType}; use crate::state::{AppState, DESKTOP_USER_ID}; use super::ApiError; // ============ Types ============ #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SavedViewInput { pub name: String, pub view_type: String, pub filters: ViewFilters, pub sort_by: Option, pub sort_order: Option, pub is_pinned: Option, } // ============ Commands ============ /// Lists all saved views for the current user. /// /// # Errors /// /// Returns `DATABASE_ERROR` if the query fails. #[tauri::command] #[instrument(skip_all)] pub async fn list_saved_views( state: State<'_, Arc>, ) -> Result, ApiError> { Ok(state.saved_views.list_all(DESKTOP_USER_ID).await?) } /// Lists only pinned views for the current user. /// /// Pinned views appear in the sidebar for quick access. /// /// # Errors /// /// Returns `DATABASE_ERROR` if the query fails. #[tauri::command] #[instrument(skip_all)] pub async fn list_pinned_views( state: State<'_, Arc>, ) -> Result, ApiError> { Ok(state.saved_views.list_pinned(DESKTOP_USER_ID).await?) } /// Retrieves a single saved view by ID. /// /// # Errors /// /// Returns `DATABASE_ERROR` if the query fails. /// Returns `None` (not an error) if the view doesn't exist. #[tauri::command] #[instrument(skip_all)] pub async fn get_saved_view( state: State<'_, Arc>, id: SavedViewId, ) -> Result, ApiError> { Ok(state.saved_views.get_by_id(id, DESKTOP_USER_ID).await?) } /// Creates a new saved view. /// /// # Arguments /// /// * `input` - View configuration: /// - `name`: Display name for the view /// - `view_type`: Type of items (tasks, emails, events) /// - `filters`: Filter criteria to apply /// - `sort_by`/`sort_order`: Optional sorting configuration /// - `is_pinned`: Whether to pin to sidebar /// /// # Errors /// /// Returns `VALIDATION_ERROR` if view_type is invalid. /// Returns `DATABASE_ERROR` if the insert fails. #[tauri::command] #[instrument(skip_all)] pub async fn create_saved_view( state: State<'_, Arc>, input: SavedViewInput, ) -> Result { let view_type = match input.view_type.as_str() { "tasks" => ViewType::Tasks, "emails" => ViewType::Emails, "events" => ViewType::Events, _ => return Err(ApiError::validation("viewType", "Invalid view type. Must be tasks, emails, or events")), }; let new_view = NewSavedView { name: input.name, view_type, filters: input.filters, sort_by: input.sort_by, sort_order: input.sort_order, is_pinned: input.is_pinned, }; Ok(state.saved_views.create(DESKTOP_USER_ID, new_view).await?) } /// Updates an existing saved view. /// /// # Errors /// /// Returns `VALIDATION_ERROR` if view_type is invalid. /// Returns `DATABASE_ERROR` if the update fails. /// Returns `None` (not an error) if the view doesn't exist. #[tauri::command] #[instrument(skip_all)] pub async fn update_saved_view( state: State<'_, Arc>, id: SavedViewId, input: SavedViewInput, ) -> Result, ApiError> { let view_type = match input.view_type.as_str() { "tasks" => ViewType::Tasks, "emails" => ViewType::Emails, "events" => ViewType::Events, _ => return Err(ApiError::validation("viewType", "Invalid view type. Must be tasks, emails, or events")), }; let new_view = NewSavedView { name: input.name, view_type, filters: input.filters, sort_by: input.sort_by, sort_order: input.sort_order, is_pinned: input.is_pinned, }; Ok(state.saved_views.update(id, DESKTOP_USER_ID, new_view).await?) } /// Deletes a saved view. /// /// # Errors /// /// Returns `DATABASE_ERROR` if the delete fails. #[tauri::command] #[instrument(skip_all)] pub async fn delete_saved_view( state: State<'_, Arc>, id: SavedViewId, ) -> Result { Ok(state.saved_views.delete(id, DESKTOP_USER_ID).await?) } /// Toggles the pinned status of a saved view. /// /// Pinned views appear in the sidebar for quick access. /// /// # Errors /// /// Returns `DATABASE_ERROR` if the update fails. /// Returns `None` (not an error) if the view doesn't exist. #[tauri::command] #[instrument(skip_all)] pub async fn toggle_view_pinned( state: State<'_, Arc>, id: SavedViewId, ) -> Result, ApiError> { Ok(state.saved_views.toggle_pinned(id, DESKTOP_USER_ID).await?) }