From 7d3ddc12ed52982ad2ad7958a57bf30637f72e3c Mon Sep 17 00:00:00 2001 From: despiegk Date: Fri, 8 Aug 2025 08:30:20 +0200 Subject: [PATCH] ... --- Cargo.toml | 2 + index.scss | 290 ++++++++++++++++++++++++++++++++++++ kanban_data.json | 139 +++++++++++++++++ public/kanban_data.json | 139 +++++++++++++++++ src/app.rs | 86 +---------- src/kanban.rs | 321 ++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + 7 files changed, 894 insertions(+), 84 deletions(-) create mode 100644 kanban_data.json create mode 100644 public/kanban_data.json create mode 100644 src/kanban.rs diff --git a/Cargo.toml b/Cargo.toml index a6388cd..a3dafd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,6 @@ yew = { version="0.21", features=["csr"] } web-sys = { version = "0.3", features = ["Document", "HtmlElement", "Window"] } gloo-utils = "0.1" gloo-storage = "0.2" +gloo-net = "0.4" +wasm-bindgen-futures = "0.4" serde = { version = "1.0", features = ["derive"] } \ No newline at end of file diff --git a/index.scss b/index.scss index 7666de6..41c234d 100644 --- a/index.scss +++ b/index.scss @@ -251,4 +251,294 @@ body { background: none !important; color: black !important; } +} + +// Kanban Board Styles +.kanban-board { + background-color: var(--bs-body-bg); + min-height: 100vh; + padding: 2rem 0; + + .kanban-header { + margin-bottom: 2rem; + text-align: center; + + h1 { + color: var(--bs-body-color); + font-weight: 700; + margin-bottom: 0.5rem; + } + + .kanban-description { + color: var(--bs-navbar-color); + font-size: 1.1rem; + } + } + + .kanban-columns { + display: flex; + gap: 1.5rem; + overflow-x: auto; + padding: 1rem 0; + min-height: 70vh; + + @media (max-width: 768px) { + flex-direction: column; + gap: 1rem; + } + } + + .kanban-column { + flex: 1; + min-width: 300px; + background-color: var(--bs-feature-bg); + border-radius: 0.75rem; + padding: 1.5rem; + box-shadow: var(--bs-shadow); + border: 1px solid var(--bs-card-border); + + .column-header { + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 2px solid var(--bs-card-border); + + .column-title { + font-size: 1.25rem; + font-weight: 600; + color: var(--bs-body-color); + margin-bottom: 0.5rem; + display: flex; + align-items: center; + gap: 0.5rem; + + .card-count { + background-color: var(--bs-navbar-color); + color: var(--bs-body-bg); + border-radius: 50%; + width: 1.5rem; + height: 1.5rem; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.75rem; + font-weight: 700; + } + } + + .column-description { + color: var(--bs-navbar-color); + font-size: 0.9rem; + margin: 0; + } + } + + .column-cards { + display: flex; + flex-direction: column; + gap: 1rem; + min-height: 200px; + } + } + + .kanban-card { + background-color: var(--bs-card-bg); + border: 1px solid var(--bs-card-border); + border-radius: 0.5rem; + padding: 1rem; + box-shadow: var(--bs-shadow); + transition: all 0.3s ease; + cursor: pointer; + + &:hover { + transform: translateY(-2px); + box-shadow: var(--bs-shadow-lg); + } + + .card-header { + margin-bottom: 0.75rem; + + .card-title { + font-size: 1rem; + font-weight: 600; + color: var(--bs-body-color); + margin-bottom: 0.5rem; + line-height: 1.3; + } + + .card-description { + color: var(--bs-navbar-color); + font-size: 0.875rem; + line-height: 1.4; + margin: 0; + } + } + + .card-meta { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 0.75rem; + + .priority-badge { + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + + &.priority-high { + background-color: rgba(220, 53, 69, 0.1); + color: #dc3545; + border: 1px solid rgba(220, 53, 69, 0.2); + } + + &.priority-medium { + background-color: rgba(255, 193, 7, 0.1); + color: #ffc107; + border: 1px solid rgba(255, 193, 7, 0.2); + } + + &.priority-low { + background-color: rgba(25, 135, 84, 0.1); + color: #198754; + border: 1px solid rgba(25, 135, 84, 0.2); + } + } + + .due-date { + background-color: rgba(var(--bs-primary-rgb), 0.1); + color: var(--bs-primary); + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-size: 0.75rem; + font-weight: 500; + border: 1px solid rgba(var(--bs-primary-rgb), 0.2); + } + } + + .card-tags { + display: flex; + flex-wrap: wrap; + gap: 0.25rem; + margin-bottom: 0.75rem; + + .tag { + background-color: var(--bs-navbar-color); + color: var(--bs-body-bg); + padding: 0.125rem 0.5rem; + border-radius: 1rem; + font-size: 0.7rem; + font-weight: 500; + opacity: 0.8; + } + } + + .card-assignee { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; + color: var(--bs-navbar-color); + font-size: 0.875rem; + + .assignee-avatar { + width: 1.5rem; + height: 1.5rem; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 0.7rem; + font-weight: 600; + } + } + + .card-stats { + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 0.75rem; + border-top: 1px solid var(--bs-card-border); + + .stat-item { + display: flex; + align-items: center; + gap: 0.25rem; + color: var(--bs-navbar-color); + font-size: 0.75rem; + + i { + font-size: 0.875rem; + } + } + + .checklist-progress { + display: flex; + align-items: center; + gap: 0.5rem; + + .progress-bar { + width: 40px; + height: 4px; + background-color: var(--bs-card-border); + border-radius: 2px; + overflow: hidden; + + .progress-fill { + height: 100%; + background-color: #198754; + transition: width 0.3s ease; + } + } + + .progress-text { + font-size: 0.7rem; + color: var(--bs-navbar-color); + } + } + } + } +} + +// Responsive kanban adjustments +@media (max-width: 992px) { + .kanban-board { + .kanban-columns { + gap: 1rem; + } + + .kanban-column { + min-width: 280px; + padding: 1rem; + } + } +} + +@media (max-width: 576px) { + .kanban-board { + padding: 1rem 0; + + .kanban-column { + min-width: 100%; + margin: 0; + } + + .kanban-card { + padding: 0.75rem; + + .card-meta { + flex-direction: column; + align-items: flex-start; + gap: 0.25rem; + } + + .card-stats { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + } + } + } } \ No newline at end of file diff --git a/kanban_data.json b/kanban_data.json new file mode 100644 index 0000000..2481224 --- /dev/null +++ b/kanban_data.json @@ -0,0 +1,139 @@ +{ + "title": "Project Management Board", + "description": "Track project progress with this kanban board", + "columns": [ + { + "id": "todo", + "title": "To Do", + "description": "Tasks that need to be started", + "cards": [ + { + "id": "card-1", + "title": "Design User Interface", + "description": "Create wireframes and mockups for the new feature", + "priority": "high", + "assignee": "Alice Johnson", + "tags": ["design", "ui/ux"], + "dueDate": "2024-01-15", + "attachments": 2, + "comments": 3, + "checklist": { + "completed": 1, + "total": 4 + } + }, + { + "id": "card-2", + "title": "Research Market Trends", + "description": "Analyze current market trends and competitor analysis", + "priority": "medium", + "assignee": "Bob Smith", + "tags": ["research", "analysis"], + "dueDate": "2024-01-20", + "attachments": 0, + "comments": 1, + "checklist": { + "completed": 0, + "total": 3 + } + } + ] + }, + { + "id": "in-progress", + "title": "In Progress", + "description": "Tasks currently being worked on", + "cards": [ + { + "id": "card-3", + "title": "Implement Authentication", + "description": "Set up user authentication system with JWT tokens and secure password handling", + "priority": "high", + "assignee": "Charlie Brown", + "tags": ["backend", "security"], + "dueDate": "2024-01-12", + "attachments": 1, + "comments": 5, + "checklist": { + "completed": 2, + "total": 5 + } + }, + { + "id": "card-4", + "title": "Database Migration", + "description": "Migrate existing data to new database schema", + "priority": "medium", + "assignee": "Diana Prince", + "tags": ["database", "migration"], + "dueDate": "2024-01-18", + "attachments": 3, + "comments": 2, + "checklist": { + "completed": 3, + "total": 6 + } + } + ] + }, + { + "id": "review", + "title": "Review", + "description": "Tasks pending review and approval", + "cards": [ + { + "id": "card-5", + "title": "API Documentation", + "description": "Complete API documentation with examples and usage guidelines", + "priority": "low", + "assignee": "Eve Wilson", + "tags": ["documentation", "api"], + "dueDate": "2024-01-10", + "attachments": 2, + "comments": 4, + "checklist": { + "completed": 4, + "total": 4 + } + } + ] + }, + { + "id": "done", + "title": "Done", + "description": "Completed tasks", + "cards": [ + { + "id": "card-6", + "title": "Setup Development Environment", + "description": "Configure development tools and environment for the team", + "priority": "high", + "assignee": "Frank Miller", + "tags": ["setup", "devops"], + "dueDate": "2024-01-05", + "attachments": 1, + "comments": 2, + "checklist": { + "completed": 3, + "total": 3 + } + }, + { + "id": "card-7", + "title": "Initial Project Planning", + "description": "Define project scope, timeline, and resource allocation", + "priority": "high", + "assignee": "Grace Lee", + "tags": ["planning", "management"], + "dueDate": "2024-01-03", + "attachments": 4, + "comments": 8, + "checklist": { + "completed": 5, + "total": 5 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/public/kanban_data.json b/public/kanban_data.json new file mode 100644 index 0000000..2481224 --- /dev/null +++ b/public/kanban_data.json @@ -0,0 +1,139 @@ +{ + "title": "Project Management Board", + "description": "Track project progress with this kanban board", + "columns": [ + { + "id": "todo", + "title": "To Do", + "description": "Tasks that need to be started", + "cards": [ + { + "id": "card-1", + "title": "Design User Interface", + "description": "Create wireframes and mockups for the new feature", + "priority": "high", + "assignee": "Alice Johnson", + "tags": ["design", "ui/ux"], + "dueDate": "2024-01-15", + "attachments": 2, + "comments": 3, + "checklist": { + "completed": 1, + "total": 4 + } + }, + { + "id": "card-2", + "title": "Research Market Trends", + "description": "Analyze current market trends and competitor analysis", + "priority": "medium", + "assignee": "Bob Smith", + "tags": ["research", "analysis"], + "dueDate": "2024-01-20", + "attachments": 0, + "comments": 1, + "checklist": { + "completed": 0, + "total": 3 + } + } + ] + }, + { + "id": "in-progress", + "title": "In Progress", + "description": "Tasks currently being worked on", + "cards": [ + { + "id": "card-3", + "title": "Implement Authentication", + "description": "Set up user authentication system with JWT tokens and secure password handling", + "priority": "high", + "assignee": "Charlie Brown", + "tags": ["backend", "security"], + "dueDate": "2024-01-12", + "attachments": 1, + "comments": 5, + "checklist": { + "completed": 2, + "total": 5 + } + }, + { + "id": "card-4", + "title": "Database Migration", + "description": "Migrate existing data to new database schema", + "priority": "medium", + "assignee": "Diana Prince", + "tags": ["database", "migration"], + "dueDate": "2024-01-18", + "attachments": 3, + "comments": 2, + "checklist": { + "completed": 3, + "total": 6 + } + } + ] + }, + { + "id": "review", + "title": "Review", + "description": "Tasks pending review and approval", + "cards": [ + { + "id": "card-5", + "title": "API Documentation", + "description": "Complete API documentation with examples and usage guidelines", + "priority": "low", + "assignee": "Eve Wilson", + "tags": ["documentation", "api"], + "dueDate": "2024-01-10", + "attachments": 2, + "comments": 4, + "checklist": { + "completed": 4, + "total": 4 + } + } + ] + }, + { + "id": "done", + "title": "Done", + "description": "Completed tasks", + "cards": [ + { + "id": "card-6", + "title": "Setup Development Environment", + "description": "Configure development tools and environment for the team", + "priority": "high", + "assignee": "Frank Miller", + "tags": ["setup", "devops"], + "dueDate": "2024-01-05", + "attachments": 1, + "comments": 2, + "checklist": { + "completed": 3, + "total": 3 + } + }, + { + "id": "card-7", + "title": "Initial Project Planning", + "description": "Define project scope, timeline, and resource allocation", + "priority": "high", + "assignee": "Grace Lee", + "tags": ["planning", "management"], + "dueDate": "2024-01-03", + "attachments": 4, + "comments": 8, + "checklist": { + "completed": 5, + "total": 5 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index 4ba1ef8..3c91001 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,6 +2,7 @@ use yew::prelude::*; use gloo_utils::document; use gloo_storage::{LocalStorage, Storage}; use serde::{Deserialize, Serialize}; +use crate::kanban::KanbanBoard; #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] enum Theme { @@ -80,90 +81,7 @@ pub fn app() -> Html { -
-
-
-
-

{"🚀 Welcome to Yew Bootstrap!"}

-

{"Experience the power of Rust and WebAssembly with this modern Yew application featuring Bootstrap 5 integration, responsive design, and seamless dark mode switching."}

-
- - -
-
-
-
-
- -
-
-
-
-
-
- -
-

{"⚡ Fast Performance"}

-

{"Built with Rust and WebAssembly for blazing fast performance. Experience near-native speed in your web applications."}

-
-
-
-
-
-
-
- -
-

{"🛡️ Type Safety"}

-

{"Rust's powerful type system ensures memory safety and prevents common programming errors at compile time."}

-
-
-
-
-
-
-
- -
-

{"📱 Responsive Design"}

-

{"Fully responsive design that works perfectly on desktop, tablet, and mobile devices with Bootstrap 5."}

-
-
-
-
-
- -
-
-
-
-

{"🎨 Modern UI Components"}

-

{"This application showcases modern UI patterns with smooth theme transitions, interactive components, and beautiful typography."}

-
    -
  • {"Dark/Light theme switching"}
  • -
  • {"Responsive navigation"}
  • -
  • {"Bootstrap 5 integration"}
  • -
  • {"Custom CSS properties"}
  • -
-
-
-
- -
-
-
-
-
- - + } } diff --git a/src/kanban.rs b/src/kanban.rs new file mode 100644 index 0000000..83ed2f0 --- /dev/null +++ b/src/kanban.rs @@ -0,0 +1,321 @@ +use yew::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct KanbanData { + pub title: String, + pub description: String, + pub columns: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct KanbanColumn { + pub id: String, + pub title: String, + pub description: String, + pub cards: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct KanbanCard { + pub id: String, + pub title: String, + pub description: String, + pub priority: String, + pub assignee: String, + pub tags: Vec, + #[serde(rename = "dueDate")] + pub due_date: String, + pub attachments: u32, + pub comments: u32, + pub checklist: ChecklistInfo, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ChecklistInfo { + pub completed: u32, + pub total: u32, +} + +fn get_sample_data() -> KanbanData { + KanbanData { + title: "Project Management Board".to_string(), + description: "Track project progress with this kanban board".to_string(), + columns: vec![ + KanbanColumn { + id: "todo".to_string(), + title: "To Do".to_string(), + description: "Tasks that need to be started".to_string(), + cards: vec![ + KanbanCard { + id: "card-1".to_string(), + title: "Design User Interface".to_string(), + description: "Create wireframes and mockups for the new feature".to_string(), + priority: "high".to_string(), + assignee: "Alice Johnson".to_string(), + tags: vec!["design".to_string(), "ui/ux".to_string()], + due_date: "2024-01-15".to_string(), + attachments: 2, + comments: 3, + checklist: ChecklistInfo { completed: 1, total: 4 }, + }, + KanbanCard { + id: "card-2".to_string(), + title: "Research Market Trends".to_string(), + description: "Analyze current market trends and competitor analysis".to_string(), + priority: "medium".to_string(), + assignee: "Bob Smith".to_string(), + tags: vec!["research".to_string(), "analysis".to_string()], + due_date: "2024-01-20".to_string(), + attachments: 0, + comments: 1, + checklist: ChecklistInfo { completed: 0, total: 3 }, + }, + ], + }, + KanbanColumn { + id: "in-progress".to_string(), + title: "In Progress".to_string(), + description: "Tasks currently being worked on".to_string(), + cards: vec![ + KanbanCard { + id: "card-3".to_string(), + title: "Implement Authentication".to_string(), + description: "Set up user authentication system with JWT tokens and secure password handling".to_string(), + priority: "high".to_string(), + assignee: "Charlie Brown".to_string(), + tags: vec!["backend".to_string(), "security".to_string()], + due_date: "2024-01-12".to_string(), + attachments: 1, + comments: 5, + checklist: ChecklistInfo { completed: 2, total: 5 }, + }, + KanbanCard { + id: "card-4".to_string(), + title: "Database Migration".to_string(), + description: "Migrate existing data to new database schema".to_string(), + priority: "medium".to_string(), + assignee: "Diana Prince".to_string(), + tags: vec!["database".to_string(), "migration".to_string()], + due_date: "2024-01-18".to_string(), + attachments: 3, + comments: 2, + checklist: ChecklistInfo { completed: 3, total: 6 }, + }, + ], + }, + KanbanColumn { + id: "review".to_string(), + title: "Review".to_string(), + description: "Tasks pending review and approval".to_string(), + cards: vec![ + KanbanCard { + id: "card-5".to_string(), + title: "API Documentation".to_string(), + description: "Complete API documentation with examples and usage guidelines".to_string(), + priority: "low".to_string(), + assignee: "Eve Wilson".to_string(), + tags: vec!["documentation".to_string(), "api".to_string()], + due_date: "2024-01-10".to_string(), + attachments: 2, + comments: 4, + checklist: ChecklistInfo { completed: 4, total: 4 }, + }, + ], + }, + KanbanColumn { + id: "done".to_string(), + title: "Done".to_string(), + description: "Completed tasks".to_string(), + cards: vec![ + KanbanCard { + id: "card-6".to_string(), + title: "Setup Development Environment".to_string(), + description: "Configure development tools and environment for the team".to_string(), + priority: "high".to_string(), + assignee: "Frank Miller".to_string(), + tags: vec!["setup".to_string(), "devops".to_string()], + due_date: "2024-01-05".to_string(), + attachments: 1, + comments: 2, + checklist: ChecklistInfo { completed: 3, total: 3 }, + }, + KanbanCard { + id: "card-7".to_string(), + title: "Initial Project Planning".to_string(), + description: "Define project scope, timeline, and resource allocation".to_string(), + priority: "high".to_string(), + assignee: "Grace Lee".to_string(), + tags: vec!["planning".to_string(), "management".to_string()], + due_date: "2024-01-03".to_string(), + attachments: 4, + comments: 8, + checklist: ChecklistInfo { completed: 5, total: 5 }, + }, + ], + }, + ], + } +} + +#[function_component(KanbanBoard)] +pub fn kanban_board() -> Html { + let data = get_sample_data(); + + html! { +
+
+
+

{&data.title}

+

{&data.description}

+
+
+ {for data.columns.iter().map(|column| { + html! { + + } + })} +
+
+
+ } +} + +#[derive(Properties, PartialEq)] +pub struct KanbanColumnProps { + pub column: KanbanColumn, +} + +#[function_component(KanbanColumnComponent)] +pub fn kanban_column_component(props: &KanbanColumnProps) -> Html { + let column = &props.column; + + html! { +
+
+
+ {&column.title} + {column.cards.len()} +
+

{&column.description}

+
+
+ {for column.cards.iter().map(|card| { + html! { + + } + })} +
+
+ } +} + +#[derive(Properties, PartialEq)] +pub struct KanbanCardProps { + pub card: KanbanCard, +} + +#[function_component(KanbanCardComponent)] +pub fn kanban_card_component(props: &KanbanCardProps) -> Html { + let card = &props.card; + + let priority_class = match card.priority.as_str() { + "high" => "priority-high", + "medium" => "priority-medium", + "low" => "priority-low", + _ => "priority-medium", + }; + + let progress_percentage = if card.checklist.total > 0 { + (card.checklist.completed as f32 / card.checklist.total as f32 * 100.0) as u32 + } else { + 0 + }; + + let assignee_initials = card.assignee + .split_whitespace() + .map(|word| word.chars().next().unwrap_or(' ')) + .collect::() + .to_uppercase(); + + html! { +
+
+

{&card.title}

+

{&card.description}

+
+ +
+ + {&card.priority} + + + + {&card.due_date} + +
+ + {if !card.tags.is_empty() { + html! { +
+ {for card.tags.iter().map(|tag| { + html! { + {tag} + } + })} +
+ } + } else { + html! {} + }} + +
+
+ {assignee_initials} +
+ {&card.assignee} +
+ +
+
+ {if card.attachments > 0 { + html! { +
+ + {card.attachments} +
+ } + } else { + html! {} + }} + + {if card.comments > 0 { + html! { +
+ + {card.comments} +
+ } + } else { + html! {} + }} +
+ + {if card.checklist.total > 0 { + html! { +
+
+
+
+ + {format!("{}/{}", card.checklist.completed, card.checklist.total)} + +
+ } + } else { + html! {} + }} +
+
+ } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c526784..114720d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod app; +mod kanban; use app::App; fn main() {