Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Backend
backend/src/*/bin/
backend/src/*/obj/
backend/tests/*/bin/
backend/tests/*/obj/
*.db
*.db-shm
*.db-wal

# Frontend
frontend/*/node_modules/
frontend/*/dist/
frontend/*/.env.local
frontend/*/.env.*.local

# IDEs
.vs/
.vscode/
.idea/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db
273 changes: 272 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,273 @@
# Taskdeck
Personal Kanban and to-do system for developers – keyboard-friendly, offline-first, and extensible.

**Taskdeck** is a personal Kanban and to-do manager designed for developers, featuring a keyboard-friendly interface, offline-first architecture, and clean design principles.

## 🎯 Features

- **Kanban Boards**: Visual management with boards → columns → cards
- **WIP Limits**: Enforce work-in-progress limits per column to maintain focus
- **Labels & Due Dates**: Organize cards with color-coded labels and track deadlines
- **Blocked Cards**: Mark cards as blocked with reasons to track impediments
- **Clean Architecture**: Backend built with Domain-Driven Design principles
- **Modern Stack**: Vue 3 + TypeScript frontend, .NET 8 + EF Core backend
- **Offline-First**: Local SQLite database, no cloud dependency required

## 📋 Tech Stack

### Backend
- **.NET 8** - Modern C# runtime
- **ASP.NET Core** - Web API framework
- **Entity Framework Core** - ORM with SQLite
- **Clean Architecture** - Domain, Application, Infrastructure, API layers
- **xUnit + FluentAssertions** - Testing framework

### Frontend
- **Vue 3** - Progressive JavaScript framework
- **Vite** - Fast build tool
- **TypeScript** - Type-safe JavaScript
- **Pinia** - State management
- **Vue Router** - Client-side routing
- **TailwindCSS** - Utility-first CSS framework
- **Axios** - HTTP client

## 🚀 Getting Started

### Prerequisites

- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)
- [Node.js 20+](https://nodejs.org/) and npm

### Backend Setup

1. Navigate to the backend directory:
```bash
cd backend
```

2. Restore dependencies:
```bash
dotnet restore
```

3. Create the database and run migrations:
```bash
dotnet ef database update -p src/Taskdeck.Infrastructure/Taskdeck.Infrastructure.csproj -s src/Taskdeck.Api/Taskdeck.Api.csproj
```

4. Run the API:
```bash
dotnet run --project src/Taskdeck.Api/Taskdeck.Api.csproj
```

The API will be available at `http://localhost:5000` (or the port specified in your configuration).

### Frontend Setup

1. Navigate to the frontend directory:
```bash
cd frontend/taskdeck-web
```

2. Install dependencies:
```bash
npm install
```

3. Start the development server:
```bash
npm run dev
```

The frontend will be available at `http://localhost:5173`.

## 🧪 Running Tests

### Backend Tests

Run domain and application tests:
```bash
cd backend
dotnet test
```

Run tests with coverage:
```bash
dotnet test /p:CollectCoverage=true
```

## 📐 Architecture

Taskdeck follows **Clean Architecture** principles with clear separation of concerns:

```
backend/
├── src/
│ ├── Taskdeck.Domain/ # Domain entities and business rules
│ │ ├── Entities/ # Board, Column, Card, Label
│ │ ├── Common/ # Base entity, Result pattern
│ │ └── Exceptions/ # Domain exceptions
│ │
│ ├── Taskdeck.Application/ # Use cases and business logic
│ │ ├── Services/ # BoardService, ColumnService, etc.
│ │ ├── DTOs/ # Data transfer objects
│ │ └── Interfaces/ # Repository interfaces
│ │
│ ├── Taskdeck.Infrastructure/ # Data access and external concerns
│ │ ├── Persistence/ # EF Core DbContext
│ │ └── Repositories/ # Repository implementations
│ │
│ └── Taskdeck.Api/ # REST API layer
│ └── Controllers/ # API endpoints
└── tests/
├── Taskdeck.Domain.Tests/
└── Taskdeck.Application.Tests/
```

```
frontend/
└── taskdeck-web/
└── src/
├── api/ # HTTP client and API calls
├── components/ # Vue components
├── router/ # Vue Router configuration
├── store/ # Pinia state management
├── types/ # TypeScript type definitions
└── views/ # Page-level components
```

## 🎨 Domain Model

### Core Entities

**Board**
- Name, description
- Contains columns and cards
- Archive functionality

**Column**
- Name, position
- Optional WIP limit
- Belongs to a board

**Card**
- Title, description
- Due date (optional)
- Position within column
- Blocked status with reason
- Multiple labels

**Label**
- Name, color (hex)
- Board-scoped
- Many-to-many with cards

### Business Rules

1. **WIP Limit Enforcement**: Cards cannot be moved to a column that has reached its WIP limit
2. **Position Management**: Cards and columns maintain ordered positions
3. **Validation**: All entities enforce validation rules (e.g., non-empty names, valid hex colors)
4. **Board Integrity**: Cards must belong to exactly one board and one column

## 🔌 API Endpoints

### Boards
- `GET /api/boards` - List all boards
- `GET /api/boards/{id}` - Get board with columns
- `POST /api/boards` - Create a new board
- `PUT /api/boards/{id}` - Update board
- `DELETE /api/boards/{id}` - Archive board

### Columns
- `GET /api/boards/{boardId}/columns` - List columns for a board
- `POST /api/boards/{boardId}/columns` - Create a column
- `PATCH /api/boards/{boardId}/columns/{columnId}` - Update column
- `DELETE /api/boards/{boardId}/columns/{columnId}` - Delete column

### Cards
- `GET /api/boards/{boardId}/cards` - List/search cards
- `POST /api/boards/{boardId}/cards` - Create a card
- `PATCH /api/boards/{boardId}/cards/{cardId}` - Update card
- `POST /api/boards/{boardId}/cards/{cardId}/move` - Move card
- `DELETE /api/boards/{boardId}/cards/{cardId}` - Delete card

### Labels
- `GET /api/boards/{boardId}/labels` - List labels for a board
- `POST /api/boards/{boardId}/labels` - Create a label
- `PATCH /api/boards/{boardId}/labels/{labelId}` - Update label
- `DELETE /api/boards/{boardId}/labels/{labelId}` - Delete label

API documentation is available via Swagger at `http://localhost:5000/swagger` when running in development mode.

## 🗂️ Database

Taskdeck uses **SQLite** for local, file-based storage. The database file (`taskdeck.db`) is created in the API project directory on first run.

### Running Migrations

Create a new migration after model changes:
```bash
dotnet ef migrations add MigrationName -p src/Taskdeck.Infrastructure/Taskdeck.Infrastructure.csproj -s src/Taskdeck.Api/Taskdeck.Api.csproj
```

Apply migrations:
```bash
dotnet ef database update -p src/Taskdeck.Infrastructure/Taskdeck.Infrastructure.csproj -s src/Taskdeck.Api/Taskdeck.Api.csproj
```

## 🛠️ Development

### Code Style

- **Backend**: Follow standard C# conventions, use `PascalCase` for public members, `camelCase` for private fields
- **Frontend**: Use TypeScript strict mode, follow Vue 3 Composition API patterns

### Key Design Patterns

- **Repository Pattern**: Abstracts data access
- **Unit of Work**: Manages transactions
- **Result Pattern**: Type-safe error handling
- **Service Layer**: Encapsulates business logic
- **DTO Pattern**: Separates API contracts from domain models

## 📈 Roadmap

### Phase 1 (Complete)
- ✅ Core domain model
- ✅ CRUD operations for all entities
- ✅ WIP limit enforcement
- ✅ Basic Vue 3 frontend
- ✅ API integration

### Phase 2 (Planned)
- [ ] Drag-and-drop for cards and columns
- [ ] Card modal for detailed editing
- [ ] Time tracking per card
- [ ] Keyboard shortcuts
- [ ] Search and filtering UI

### Phase 3 (Future)
- [ ] CLI client
- [ ] Recurring tasks
- [ ] Analytics dashboard
- [ ] Dark mode
- [ ] Multi-user support (optional)
- [ ] Sync to remote server (optional)

## 🤝 Contributing

This is primarily a personal learning project, but feedback and suggestions are welcome!

## 📄 License

MIT License - feel free to use this project as a reference or starting point for your own Kanban tool.

## 🙏 Acknowledgments

- Inspired by Trello, Jira, and other Kanban tools
- Built following Clean Architecture principles by Robert C. Martin
- Uses modern best practices for .NET and Vue.js development

---

**Happy task tracking!** 🎯
61 changes: 61 additions & 0 deletions backend/Taskdeck.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8A7E8B9C-1D2E-4F3A-9B5C-6D7E8F9A0B1C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{9B8F9CAD-2E3F-5G4B-AC6D-7E8F9GA1C2D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Taskdeck.Domain", "src\Taskdeck.Domain\Taskdeck.Domain.csproj", "{A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Taskdeck.Application", "src\Taskdeck.Application\Taskdeck.Application.csproj", "{B2C3D4E5-F6A7-5B6C-9D0E-1F2A3B4C5D6E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Taskdeck.Infrastructure", "src\Taskdeck.Infrastructure\Taskdeck.Infrastructure.csproj", "{C3D4E5F6-A7B8-6C7D-0E1F-2A3B4C5D6E7F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Taskdeck.Api", "src\Taskdeck.Api\Taskdeck.Api.csproj", "{D4E5F6A7-B8C9-7D8E-1F2A-3B4C5D6E7F8A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Taskdeck.Domain.Tests", "tests\Taskdeck.Domain.Tests\Taskdeck.Domain.Tests.csproj", "{E5F6A7B8-C9D0-8E9F-2A3B-4C5D6E7F8A9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Taskdeck.Application.Tests", "tests\Taskdeck.Application.Tests\Taskdeck.Application.Tests.csproj", "{F6A7B8C9-D0E1-9FA0-3B4C-5D6E7F8A9B0C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|Any CPU.Build.0 = Release|Any CPU
{B2C3D4E5-F6A7-5B6C-9D0E-1F2A3B4C5D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2C3D4E5-F6A7-5B6C-9D0E-1F2A3B4C5D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2C3D4E5-F6A7-5B6C-9D0E-1F2A3B4C5D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2C3D4E5-F6A7-5B6C-9D0E-1F2A3B4C5D6E}.Release|Any CPU.Build.0 = Release|Any CPU
{C3D4E5F6-A7B8-6C7D-0E1F-2A3B4C5D6E7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3D4E5F6-A7B8-6C7D-0E1F-2A3B4C5D6E7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3D4E5F6-A7B8-6C7D-0E1F-2A3B4C5D6E7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3D4E5F6-A7B8-6C7D-0E1F-2A3B4C5D6E7F}.Release|Any CPU.Build.0 = Release|Any CPU
{D4E5F6A7-B8C9-7D8E-1F2A-3B4C5D6E7F8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4E5F6A7-B8C9-7D8E-1F2A-3B4C5D6E7F8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4E5F6A7-B8C9-7D8E-1F2A-3B4C5D6E7F8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4E5F6A7-B8C9-7D8E-1F2A-3B4C5D6E7F8A}.Release|Any CPU.Build.0 = Release|Any CPU
{E5F6A7B8-C9D0-8E9F-2A3B-4C5D6E7F8A9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E5F6A7B8-C9D0-8E9F-2A3B-4C5D6E7F8A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5F6A7B8-C9D0-8E9F-2A3B-4C5D6E7F8A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5F6A7B8-C9D0-8E9F-2A3B-4C5D6E7F8A9B}.Release|Any CPU.Build.0 = Release|Any CPU
{F6A7B8C9-D0E1-9FA0-3B4C-5D6E7F8A9B0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6A7B8C9-D0E1-9FA0-3B4C-5D6E7F8A9B0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6A7B8C9-D0E1-9FA0-3B4C-5D6E7F8A9B0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6A7B8C9-D0E1-9FA0-3B4C-5D6E7F8A9B0C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D} = {8A7E8B9C-1D2E-4F3A-9B5C-6D7E8F9A0B1C}
{B2C3D4E5-F6A7-5B6C-9D0E-1F2A3B4C5D6E} = {8A7E8B9C-1D2E-4F3A-9B5C-6D7E8F9A0B1C}
{C3D4E5F6-A7B8-6C7D-0E1F-2A3B4C5D6E7F} = {8A7E8B9C-1D2E-4F3A-9B5C-6D7E8F9A0B1C}
{D4E5F6A7-B8C9-7D8E-1F2A-3B4C5D6E7F8A} = {8A7E8B9C-1D2E-4F3A-9B5C-6D7E8F9A0B1C}
{E5F6A7B8-C9D0-8E9F-2A3B-4C5D6E7F8A9B} = {9B8F9CAD-2E3F-5G4B-AC6D-7E8F9GA1C2D}
{F6A7B8C9-D0E1-9FA0-3B4C-5D6E7F8A9B0C} = {9B8F9CAD-2E3F-5G4B-AC6D-7E8F9GA1C2D}
EndGlobalSection
EndGlobal
Loading
Loading