Files
taskpile/backend/src/models.rs
Alvis f1d51b8cc8 Add side panels, task selection, graph animation, and project docs
- Foldable left panel (user profile) and right panel (task details)
- Clicking a task in the list or graph node selects it and shows details
- Both views (task list + graph) always mounted via absolute inset-0 for
  correct canvas dimensions; tabs toggle visibility with opacity
- Graph node selection animation: other nodes repel outward (charge -600),
  then selected node smoothly slides to center (500ms cubic ease-out),
  then charge restores to -120 and graph stabilizes
- Graph re-fits on tab switch and panel resize via ResizeObserver
- Fix UUID string IDs throughout (backend returns UUIDs, not integers)
- Add TaskDetailPanel, UserPanel components
- Add CLAUDE.md project documentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 11:23:06 +00:00

78 lines
1.9 KiB
Rust

use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct Task {
pub id: String,
pub title: String,
pub description: Option<String>,
pub completed: bool,
pub created_at: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphNode {
pub id: String,
pub label: String,
pub completed: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphEdge {
pub source: String,
pub target: String,
pub weight: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphData {
pub nodes: Vec<GraphNode>,
pub edges: Vec<GraphEdge>,
}
#[derive(Debug, Deserialize)]
pub struct CreateTask {
pub title: String,
pub description: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct UpdateTask {
pub title: Option<String>,
pub description: Option<String>,
pub completed: Option<bool>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_task_serialization() {
let task = Task {
id: "abc-123".to_string(),
title: "Test task".to_string(),
description: Some("A description".to_string()),
completed: false,
created_at: 1234567890,
};
let json = serde_json::to_string(&task).unwrap();
let deserialized: Task = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.id, task.id);
assert_eq!(deserialized.title, task.title);
assert_eq!(deserialized.completed, task.completed);
}
#[test]
fn test_task_no_description() {
let task = Task {
id: "xyz".to_string(),
title: "No desc".to_string(),
description: None,
completed: true,
created_at: 0,
};
let json = serde_json::to_string(&task).unwrap();
assert!(json.contains("\"description\":null"));
}
}