feat: add favorites table, CRUD commands, and Pinia store

This commit is contained in:
Your Name
2026-02-18 02:02:57 +02:00
parent 85c20247f5
commit 1ee4562647
4 changed files with 132 additions and 0 deletions

View File

@@ -677,3 +677,61 @@ pub fn get_project_budget_status(state: State<AppState>, project_id: i64) -> Res
"percent_amount": project_row.1.map(|b| if b > 0.0 { (amount_used / b) * 100.0 } else { 0.0 }) "percent_amount": project_row.1.map(|b| if b > 0.0 { (amount_used / b) * 100.0 } else { 0.0 })
})) }))
} }
// Favorite structs and commands
#[derive(Debug, Serialize, Deserialize)]
pub struct Favorite {
pub id: Option<i64>,
pub project_id: i64,
pub task_id: Option<i64>,
pub description: Option<String>,
pub sort_order: i32,
}
#[tauri::command]
pub fn get_favorites(state: State<AppState>) -> Result<Vec<Favorite>, String> {
let conn = state.db.lock().map_err(|e| e.to_string())?;
let mut stmt = conn.prepare(
"SELECT id, project_id, task_id, description, sort_order FROM favorites ORDER BY sort_order"
).map_err(|e| e.to_string())?;
let favs = stmt.query_map([], |row| {
Ok(Favorite {
id: Some(row.get(0)?),
project_id: row.get(1)?,
task_id: row.get(2)?,
description: row.get(3)?,
sort_order: row.get(4)?,
})
}).map_err(|e| e.to_string())?;
favs.collect::<Result<Vec<_>, _>>().map_err(|e| e.to_string())
}
#[tauri::command]
pub fn create_favorite(state: State<AppState>, fav: Favorite) -> Result<i64, String> {
let conn = state.db.lock().map_err(|e| e.to_string())?;
conn.execute(
"INSERT INTO favorites (project_id, task_id, description, sort_order) VALUES (?1, ?2, ?3, ?4)",
params![fav.project_id, fav.task_id, fav.description, fav.sort_order],
).map_err(|e| e.to_string())?;
Ok(conn.last_insert_rowid())
}
#[tauri::command]
pub fn delete_favorite(state: State<AppState>, id: i64) -> Result<(), String> {
let conn = state.db.lock().map_err(|e| e.to_string())?;
conn.execute("DELETE FROM favorites WHERE id = ?1", params![id])
.map_err(|e| e.to_string())?;
Ok(())
}
#[tauri::command]
pub fn reorder_favorites(state: State<AppState>, ids: Vec<i64>) -> Result<(), String> {
let conn = state.db.lock().map_err(|e| e.to_string())?;
for (i, id) in ids.iter().enumerate() {
conn.execute(
"UPDATE favorites SET sort_order = ?1 WHERE id = ?2",
params![i as i32, id],
).map_err(|e| e.to_string())?;
}
Ok(())
}

View File

@@ -158,6 +158,19 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
[], [],
)?; )?;
conn.execute(
"CREATE TABLE IF NOT EXISTS favorites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER NOT NULL,
task_id INTEGER,
description TEXT,
sort_order INTEGER DEFAULT 0,
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE SET NULL
)",
[],
)?;
conn.execute( conn.execute(
"CREATE TABLE IF NOT EXISTS settings ( "CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY, key TEXT PRIMARY KEY,

View File

@@ -73,6 +73,10 @@ pub fn run() {
commands::get_entry_tags, commands::get_entry_tags,
commands::set_entry_tags, commands::set_entry_tags,
commands::get_project_budget_status, commands::get_project_budget_status,
commands::get_favorites,
commands::create_favorite,
commands::delete_favorite,
commands::reorder_favorites,
]) ])
.setup(|app| { .setup(|app| {
#[cfg(desktop)] #[cfg(desktop)]

57
src/stores/favorites.ts Normal file
View File

@@ -0,0 +1,57 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { invoke } from '@tauri-apps/api/core'
export interface Favorite {
id?: number
project_id: number
task_id?: number | null
description?: string | null
sort_order: number
}
export const useFavoritesStore = defineStore('favorites', () => {
const favorites = ref<Favorite[]>([])
async function fetchFavorites() {
try {
favorites.value = await invoke<Favorite[]>('get_favorites')
} catch (error) {
console.error('Failed to fetch favorites:', error)
}
}
async function createFavorite(fav: Favorite): Promise<number | null> {
try {
const id = await invoke<number>('create_favorite', { fav })
favorites.value.push({ ...fav, id: Number(id) })
return Number(id)
} catch (error) {
console.error('Failed to create favorite:', error)
return null
}
}
async function deleteFavorite(id: number): Promise<boolean> {
try {
await invoke('delete_favorite', { id })
favorites.value = favorites.value.filter(f => f.id !== id)
return true
} catch (error) {
console.error('Failed to delete favorite:', error)
return false
}
}
async function reorderFavorites(ids: number[]): Promise<boolean> {
try {
await invoke('reorder_favorites', { ids })
return true
} catch (error) {
console.error('Failed to reorder favorites:', error)
return false
}
}
return { favorites, fetchFavorites, createFavorite, deleteFavorite, reorderFavorites }
})