A modern task management application built with SvelteKit, TypeScript, and Firebase. This application provides a comprehensive solution for managing tasks, goals, and team collaboration with a clean, intuitive interface.
src/
├── lib/
│ ├── core/ # Core services (Firebase, Stripe)
│ ├── data/ # Data layer and mock data
│ ├── services/ # Business logic services
│ ├── shared/ # Shared components and utilities
│ │ ├── components/ # Reusable UI components
│ │ ├── constants/ # Application constants
│ │ ├── layout/ # Layout components
│ │ └── models/ # TypeScript interfaces
│ └── stores/ # Svelte stores for state management
├── routes/ # SvelteKit pages and layouts
│ ├── tasks/ # Task management pages
│ ├── goals/ # Goal tracking pages
│ ├── components/ # Component showcase
│ └── ... # Other feature pages
└── assets/ # Static assets
Clone the repository
Install dependencies:
yarn install
Set up environment variables:
cp env.example .env
Update .env with your Firebase configuration:
VITE_FIREBASE_API_KEY=your_api_key
VITE_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=your_project_id
VITE_FIREBASE_STORAGE_BUCKET=your_project.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
VITE_FIREBASE_APP_ID=your_app_id
# Start development server
yarn dev
# Build for production
yarn build
# Preview production build
yarn preview
# Type checking
yarn check
# Watch mode for type checking
yarn check:watch
/): Overview of recent tasks and upcoming goals/tasks): Complete task management with CRUD operations/goals): Goal setting and progress tracking/components): Showcase of reusable UI components/projects): Project management (coming soon)/decisions): Decision tracking (coming soon)/sop-vault): Standard Operating Procedures (coming soon)/ad-hoc): Ad-hoc task management (coming soon)/constraint-buster): Constraint identification (coming soon)/vendor-loop): Vendor management (coming soon)The application includes a comprehensive component library with:
The application uses well-defined TypeScript interfaces:
.env filesrc/lib/shared/components/src/lib/stores/)src/lib/services/)src/app.cssThis directory contains all data-related files including real data services and data models. The data layer provides a clean separation between the UI components and the data sources, making it easier to maintain and test the application.
Mock data has been deprecated and replaced with real data services. See mock/DEPRECATED.md for migration details.
The application now uses real data services located in src/lib/services/:
src/lib/services/task/taskService.ts) - Handles all task operationssrc/lib/services/goals/task/goalService.ts) - Handles all goal operations src/lib/services/user/userService.ts) - Handles all user operationssrc/lib/services/business/businessService.ts) - Handles business operationssrc/lib/services/billing/billingService.ts) - Handles billing operationssrc/lib/services/dataService.ts) - Utility functions for data manipulationImport real data services in your components:
// Import services
import { TaskService } from '$lib/services/task/taskService';
import { GoalService } from '$lib/services/goals/task/goalService';
import { UserService } from '$lib/services/user/userService';
// Or import from the central index
import { TaskService, GoalService, UserService } from '$lib/services';
// Use utility functions
import { getRecentTasks } from '$lib/services/dataService';
getAllTasks() - Get all tasksgetTasks(userId) - Get tasks for a specific usergetTask(id) - Get a single task by IDcreateTask(task) - Create a new taskupdateTask(id, updates) - Update an existing taskdeleteTask(id) - Delete a tasktoggleTaskCompletion(id) - Toggle task completion statusgetGoals(userId) - Get goals for a specific usergetGoal(id) - Get a single goal by IDcreateGoal(goal) - Create a new goalupdateGoal(id, updates) - Update an existing goaldeleteGoal(id) - Delete a goalupdateGoalProgress(id, progress) - Update goal progressgetAllUsers() - Get all usersgetUser(id) - Get a single user by IDgetUserByEmail(email) - Get user by emailcreateUser(user) - Create a new userupdateUser(id, updates) - Update an existing userdeleteUser(id) - Delete a userAll services use TypeScript interfaces defined in src/lib/shared/models/:
Task: Complete task object with all propertiesGoal: Complete goal object with all propertiesUser: User object with authentication and profile dataBusiness: Business/workspace objectBusinessUser: User within a business contextAll services integrate with Firebase Firestore:
This service provides complete CRUD (Create, Read, Update, Delete) operations for business tasks using Firebase Firestore. The service includes comprehensive error handling, data validation, visibility logic, and permission-based access control.
The BusinessTaskService implements the endpoint specification for /businesses/{businessId}/tasks/{taskId} with the following core features:
private, team, or business visibilityThe service works with the BusinessTask interface:
interface BusinessTask {
id: string;
title: string;
description?: string;
createdBy: string; // User ID who created the task
assignedTo: string[]; // Array of user IDs assigned to the task
teamId?: string; // Optional team association
visibility: "private" | "team" | "business";
createdAt: Date;
updatedAt: Date;
dueDate?: Date;
startedDate?: Date;
dateCompleted?: Date;
priority: 'low' | 'medium' | 'high';
status: 'not-started' | 'in-progress' | 'completed' | 'overdue' | 'cancelled' | 'paused';
businessId: string;
}
Endpoint: POST /businesses/{businessId}/tasks
import { BusinessTaskService } from '$lib/services';
const newTask = await BusinessTaskService.createBusinessTask('business123', {
title: 'Complete project documentation',
description: 'Write comprehensive documentation for the new feature',
createdBy: 'user123',
assignedTo: ['user123', 'user456'],
teamId: 'team789', // Optional
visibility: 'team',
priority: 'high',
status: 'not-started',
dueDate: new Date('2024-12-31')
});
if (newTask.success) {
console.log('Task created:', newTask.data);
} else {
console.error('Error:', newTask.error);
}
Endpoint: GET /businesses/{businessId}/tasks/{taskId}
const task = await BusinessTaskService.getBusinessTask(
'business123',
'task456',
'user123'
);
if (task.success) {
console.log('Task:', task.data);
} else {
console.error('Error:', task.error);
}
Endpoint: PUT /businesses/{businessId}/tasks/{taskId}
const updated = await BusinessTaskService.updateBusinessTask(
'business123',
'task456',
{
title: 'Updated task title',
priority: 'medium',
status: 'in-progress'
},
'user123'
);
if (updated.success) {
console.log('Task updated:', updated.data);
} else {
console.error('Error:', updated.error);
}
Endpoint: DELETE /businesses/{businessId}/tasks/{taskId}
const deleted = await BusinessTaskService.deleteBusinessTask(
'business123',
'task456',
'user123'
);
if (deleted.success) {
console.log('Task deleted successfully');
} else {
console.error('Error:', deleted.error);
}
The service implements comprehensive visibility logic:
business-read and business-full: Can view all business tasksmulti-team: Can view tasks from their visible teamsteam: Can only view tasks from their own teamsThe service integrates with the existing permission system:
teamId is providedPermissionService.canViewTask() for comprehensive visibility checksThis service provides complete CRUD (Create, Read, Update, Delete) operations for tasks using Firebase Firestore. The service includes comprehensive error handling, data validation, and support for advanced querying operations.
import { TaskService } from '$lib/services/task/taskService';
import type { Task } from '$lib/shared/models';
// Example user ID (you'll get this from Firebase Auth or user store)
const userId = 'user123';
The TaskService provides the following methods:
createTask(taskData) - Create a new taskgetTask(taskId) - Get a single task by IDgetAllTasks() - Get all tasks (admin only)getTasks(userId) - Get tasks for a specific userupdateTask(taskId, updates) - Update an existing taskdeleteTask(taskId) - Delete a tasktoggleTaskCompletion(taskId) - Toggle task completion statusgetTasksByStatus(userId, completed) - Get tasks by completion statusgetTasksByPriority(userId, priority) - Get tasks by priority levelgetTasksByDateRange(userId, startDate, endDate) - Get tasks within date rangegetOverdueTasks(userId) - Get overdue tasksgetRecentTasks(limit) - Get recent tasks for dashboardconst newTask = await TaskService.createTask({
title: 'Complete project documentation',
description: 'Write comprehensive documentation for the new feature',
completed: false,
createdBy: userId,
assignedTo: [userId, 'user456'],
dueDate: new Date('2024-12-31'),
priority: 'high',
status: 'not-started'
});
if (newTask.success) {
console.log('Task created:', newTask.data);
} else {
console.error('Error:', newTask.error);
}
const tasks = await TaskService.getTasks(userId);
if (tasks.success) {
console.log('All tasks:', tasks.data);
} else {
console.error('Error:', tasks.error);
}
const task = await TaskService.getTask('taskId123');
if (task.success) {
console.log('Task:', task.data);
} else {
console.error('Error:', task.error);
}
// Get completed tasks
const completedTasks = await TaskService.getTasksByStatus(userId, true);
// Get incomplete tasks
const incompleteTasks = await TaskService.getTasksByStatus(userId, false);
const highPriorityTasks = await TaskService.getTasksByPriority(userId, 'high');
const mediumPriorityTasks = await TaskService.getTasksByPriority(userId, 'medium');
const lowPriorityTasks = await TaskService.getTasksByPriority(userId, 'low');
const updatedTask = await TaskService.updateTask('taskId123', {
title: 'Updated task title',
description: 'Updated description',
priority: 'medium',
status: 'in-progress',
assignedTo: ['user123', 'user456', 'user789']
});
if (updatedTask.success) {
console.log('Task updated:', updatedTask.data);
} else {
console.error('Error:', updatedTask.error);
}
const deleteResult = await TaskService.deleteTask('taskId123');
if (deleteResult.success) {
console.log('Task deleted successfully');
} else {
console.error('Error:', deleteResult.error);
}
const toggleResult = await TaskService.toggleTaskCompletion('taskId123');
if (toggleResult.success) {
console.log('Task completion toggled:', toggleResult.data);
} else {
console.error('Error:', toggleResult.error);
}
All methods return an ApiResponse<T> object with the following structure:
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
message?: string;
}
The service includes comprehensive error handling:
The service validates the following:
Make sure your Firestore security rules allow authenticated users to access their own tasks:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /tasks/{taskId} {
// Users can read tasks they created or are assigned to
allow read: if request.auth != null &&
(request.auth.uid == resource.data.createdBy ||
request.auth.uid in resource.data.assignedTo);
// Users can create tasks
allow create: if request.auth != null &&
request.auth.uid == request.resource.data.createdBy;
// Users can update tasks they created or are assigned to
allow update: if request.auth != null &&
(request.auth.uid == resource.data.createdBy ||
request.auth.uid in resource.data.assignedTo);
// Only task creators can delete tasks
allow delete: if request.auth != null &&
request.auth.uid == resource.data.createdBy;
}
}
}
This directory contains all reusable components for the Done. application. All components are built with Svelte 5 and styled with Tailwind CSS. The component library follows a consistent design system and provides comprehensive functionality for task management, user interaction, and data display.
A comprehensive task display component that shows all task information including title, description, metadata, status, and user information. Features drag-and-drop support and interactive elements.
Props:
task (UITask): Task data object with UI-specific propertiesisSelected (boolean): Whether the task is selectedonSelect (function): Callback when task is selectedonEdit (function): Callback when task is editedonDelete (function): Callback when task is deleteddraggable (boolean): Whether the task can be draggedUsage:
<TaskCard
task={taskData}
isSelected={false}
onSelect={(taskId) => console.log('Selected:', taskId)}
onEdit={(taskId) => console.log('Edit:', taskId)}
onDelete={(taskId) => console.log('Delete:', taskId)}
draggable={true}
/>
Container component for displaying multiple TaskCard components with proper spacing, empty state handling, and drag-and-drop support.
Props:
tasks (UITask[]): Array of task objectsselectedTasks (string[]): Array of selected task IDsonTaskSelect (function): Callback when tasks are selectedonTaskEdit (function): Callback when tasks are editedonTaskDelete (function): Callback when tasks are deletedonTaskReorder (function): Callback when tasks are reordered via drag-and-dropUsage:
<TaskList
tasks={tasks}
selectedTasks={selectedTaskIds}
onTaskSelect={handleTaskSelect}
onTaskEdit={handleTaskEdit}
onTaskDelete={handleTaskDelete}
onTaskReorder={handleTaskReorder}
/>
A slide-out drawer component for creating and editing tasks with a comprehensive form interface.
Props:
isOpen (boolean): Whether the drawer is opentask (UITask | null): Task to edit (null for new task)onClose (function): Callback when drawer is closedonSave (function): Callback when task is savedonDelete (function): Callback when task is deletedUsage:
<TaskDrawer
isOpen={isDrawerOpen}
task={editingTask}
onClose={() => isDrawerOpen = false}
onSave={handleTaskSave}
onDelete={handleTaskDelete}
/>
Displays task status with appropriate colors and optional status dot. Supports all task statuses including cancelled and paused.
Props:
status (string): 'not-started' | 'in-progress' | 'completed' | 'overdue' | 'cancelled' | 'paused'showDot (boolean): Whether to show the status dotsize (string): 'sm' | 'md' | 'lg'Usage:
<StatusBadge status="in-progress" showDot={true} size="md" />
Displays task priority with appropriate colors.
Props:
priority (string): 'low' | 'medium' | 'high'size (string): 'sm' | 'md' | 'lg'Usage:
<PriorityBadge priority="medium" size="md" />
Button component for displaying attachment counts with a paperclip icon.
Props:
count (number): Number of attachmentssize (string): 'sm' | 'md' | 'lg'onClick (function): Click handlerUsage:
<AttachmentButton count={3} size="md" onClick={handleAttachmentClick} />
Displays user profile picture or initials with fallback.
Props:
user (object): User object with name and optional avatarsize (string): 'xs' | 'sm' | 'md' | 'lg' | 'xl'showTooltip (boolean): Whether to show tooltip on hoverUsage:
<UserAvatar
user={{ name: 'John Doe', avatar: '/path/to/avatar.jpg' }}
size="md"
showTooltip={true}
/>
Displays multiple user avatars in an overlapping style with overflow handling.
Props:
users (array): Array of user objectsmaxVisible (number): Maximum number of visible avatarssize (string): 'xs' | 'sm' | 'md' | 'lg' | 'xl'showTooltip (boolean): Whether to show tooltipsUsage:
<UserGroup
users={userArray}
maxVisible={3}
size="md"
showTooltip={true}
/>
Formatted date display component for start and due dates with overdue highlighting.
Props:
startDate (string): Start date stringdueDate (string): Due date stringshowLabels (boolean): Whether to show "Start" and "Due" labelssize (string): 'sm' | 'md' | 'lg'isOverdue (boolean): Whether to force overdue stylingUsage:
<DateDisplay
startDate="2025-05-16"
dueDate="2025-05-26"
showLabels={true}
size="md"
/>
Displays @mentions in text with proper styling and click handling.
Props:
mention (string): The mention text (e.g., "@Fred")onClick (function): Click handlersize (string): 'sm' | 'md' | 'lg'Usage:
<MentionTag
mention="@Fred"
onClick={(mention) => console.log('Clicked:', mention)}
size="md"
/>
Shows when items are linked to other items with a chain icon.
Props:
isLinked (boolean): Whether the item is linkedlinkText (string): Text to displaysize (string): 'sm' | 'md' | 'lg'Usage:
<LinkIndicator isLinked={true} linkText="Linked to" size="md" />
Drag handle component for drag and drop functionality.
Props:
size (string): 'sm' | 'md' | 'lg'onClick (function): Click handlerUsage:
<DragHandle size="md" onClick={handleDragStart} />
Header component with title, search, and action buttons. Provides consistent navigation across the application.
Props:
title (string): Page titleshowSearch (boolean): Whether to show search barshowAddButton (boolean): Whether to show add buttononSearch (function): Callback when search is performedonAdd (function): Callback when add button is clickedUsage:
<TopBar
title="Tasks"
showSearch={true}
showAddButton={true}
onSearch={handleSearch}
onAdd={handleAddTask}
/>
Filter tabs component for task filtering with active state management.
Props:
activeFilter (string): Currently active filterfilters (array): Array of filter objects with id and labelonFilterChange (function): Callback when filter changesUsage:
<FilterTabs
bind:activeFilter
filters={[
{ id: 'all', label: 'All' },
{ id: 'my-tasks', label: 'My Tasks' },
{ id: 'completed', label: 'Completed' }
]}
onFilterChange={handleFilterChange}
/>
Navigation sidebar component with menu items and user information.
Props:
activePage (string): Currently active pageonPageChange (function): Callback when page changesuser (User): Current user informationUsage:
<Sidebar
activePage="tasks"
onPageChange={handlePageChange}
user={currentUser}
/>
Main header component with branding and user controls.
Props:
title (string): Application titleuser (User): Current user informationonLogout (function): Callback when user logs outUsage:
<Header
title="Done."
user={currentUser}
onLogout={handleLogout}
/>
Footer component with links and copyright information.
Props:
links (array): Array of footer linkscopyright (string): Copyright textUsage:
<Footer
links={footerLinks}
copyright="© 2024 Done. All rights reserved."
/>
interface Task {
id: string;
orderId: number; // Auto-generated order ID (1 + count of user's tasks)
title: string;
description?: string;
completed: boolean;
createdBy: string; // User ID
assignedTo: string[]; // Array of user IDs
createdAt: Date;
updatedAt: Date;
dueDate?: Date;
startedDate?: Date;
dateCompleted?: Date; // Date when task was completed
priority: 'low' | 'medium' | 'high';
status: 'not-started' | 'in-progress' | 'completed' | 'overdue' | 'cancelled' | 'paused';
}
interface UITask extends Task {
startDate: string; // Planned start date
isLinked: boolean;
attachments: number;
createdByUser: {
name: string;
avatar?: string;
};
mentions?: string[];
}
interface User {
id: string;
name: string;
email: string;
displayName?: string;
photoURL?: string;
createdAt: Date;
updatedAt: Date;
role: string;
isAdmin: boolean;
department: string;
}
All components use Tailwind CSS classes and follow a consistent design system:
blue-600)green-600)yellow-600)red-600)gray-600)Components include comprehensive accessibility features:
Components are built for modern browsers with:
Components are optimized for performance:
This directory contains comprehensive mock data for development and testing purposes. The mock data is designed to simulate a real-world task management application with realistic user relationships, task assignments, and goal tracking.
users.ts - Mock user data with IDs, names, roles, departments, and avatarstasks.ts - Mock task data with user relationships, priorities, and statusesgoals.ts - Mock goal data with user relationships and progress trackingdashboard.ts - Mock dashboard data with aggregated viewsindex.ts - Central exports and utility functions for data manipulationEach user has the following structure:
interface MockUser {
id: string; // Unique user identifier (e.g., 'user-1')
name: string; // User's display name
avatar?: string; // Optional avatar URL
email?: string; // User's email address
role?: string; // User's role in the organization
department?: string; // User's department
}
The mock data includes users from different departments and roles:
Tasks and goals include comprehensive user relationship data:
createdBy: User ID of the person who created the task/goalassignedTo: Array of user IDs assigned to the task/goalcreatedByUser: Object containing the creator's name and avatar for displaystatus: Current status (not-started, in-progress, completed, overdue, cancelled, paused)priority: Priority level (low, medium, high)dueDate: Optional due date for tasksprogress: Progress percentage for goals (0-100)The following utility functions are available for working with user data:
getUserById(userId: string)Returns a user object by their ID, or undefined if not found.
getUsersByIds(userIds: string[])Returns an array of user objects for the given user IDs.
getCreatedByUser(userId: string)Returns user display information (name and avatar) for the createdBy field.
getAssignedUsers(userIds: string[])Returns an array of user objects for assigned users.
import { getUserById, getUsersByIds, mockTasks } from '$lib/data/mock';
// Get a specific user
const user = getUserById('user-1');
// Get assigned users for a task
const task = mockTasks[0];
const assignedUsers = getUsersByIds(task.assignedTo);
// Display user names
const assignedNames = assignedUsers.map(u => u.name).join(', ');
All mock data is designed to be consistent and realistic:
createdBy and assignedTo fields correspond to actual users in the users arraycreatedByUser object provides immediate access to creator information without additional lookupsThe mock data is used throughout the application for:
The mock data integrates seamlessly with the application's service layer:
This project is licensed under the MIT License.