A hierarchical productivity platform that combines task management, time tracking, and analytics in one cohesive system.
Mullet helps you organize work across custom contexts (Modes), break down high-level goals into actionable hierarchies, and track exactly where your time goes. Whether you're managing client projects, personal goals, or fitness routines, Mullet adapts to your workflow.
- ποΈ Hierarchical Planning β Organize tasks in a four-level structure: Mode β Goal β Project/Milestone β Task
- β±οΈ Integrated Timer β Built-in stopwatch and countdown timer with session persistence and resume-from-entry
- π Analytics Dashboard β Visual time breakdown with drill-down, date filtering, and hierarchical attribution
- π€ Collaboration Tools β Comments, notes, and file attachments per entity
- π Dual Views β List-based dashboard with collapsible trees and calendar view with drag-and-drop
- π Templates β Reusable project and milestone templates for repeatable workflows
- β‘ Batch Operations β Multi-select and bulk-edit tasks, projects, milestones, and goals
- π± Responsive Design β Mobile-first UI with touch-optimized interactions
- Framework: React 18 + Next.js 14 (App Router)
- Language: TypeScript
- State Management: Zustand (global state), React Query (server state)
- Styling: Tailwind CSS
- UI Components: Radix UI primitives
- Drag & Drop: DnD Kit
- Charts: Recharts
- Date Handling: date-fns
- Framework: Django REST Framework
- Database: PostgreSQL
- Authentication: Token-based auth
Contexts for your work (e.g., Work, Personal, Fitness). Each mode has its own color and contains all other entities.
High-level objectives within a mode. Goals can contain projects, milestones, and tasks.
Major initiatives that can contain milestones and tasks. Projects support nesting (sub-projects).
Significant waypoints within projects or goals. Milestones support nesting and can contain tasks.
Actionable items. Tasks can be standalone or nested under milestones, projects, or goals.
- Transitive Lineage Resolution: Automatic parent-child relationship inference
- Effective Lineage Helpers: Climb ancestry chains to derive missing parents
- Smart Cascading: Forms automatically validate and reconcile parent-child selections
- Dual Modes: Stopwatch (open-ended) and Timer (countdown with alert)
- Session Persistence: Timer state survives page refresh via localStorage
- Per-Mode Snapshots: Each mode remembers its last timer selection
- Resume-from-Entry: Click any past session to resume with remaining time
- Live Retargeting: Switch what you're timing mid-session without stopping
- Optimistic Updates: UI updates immediately, syncs in background
- Granular Stores: Separate Zustand stores for entities, UI state, timer, batch editor
- React Query Integration: Server state caching with automatic invalidation
- Offline-First: Local mutations persist and sync when connection returns
- Multi-Context: Separate DnD implementations for dashboard and calendar
- Entity-Specific Constraints: Tasks can't be dragged if scheduled, milestones validate parent relationships
- Visual Feedback: Drag overlays show entity type and color
- Hierarchical Breakdown: Time rolls up from tasks β milestones β projects β goals β modes
- Chain-Up Functionality: Reassign direct time from child to parent entities
- Date Filtering: Today, This Week, This Month, All Time, or custom range
- Drill-Down Views: Click any entity to see its time breakdown
- Node.js 18+
- Python 3.10+
- PostgreSQL 14+
- Clone the repository
git clone https://github.com/ciaranjkieran/mullet.git
cd mullet- Install frontend dependencies
npm install- Set up backend (Django)
cd backend
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
python manage.py migrate
python manage.py createsuperuser- Configure environment variables
# Frontend (.env.local)
NEXT_PUBLIC_API_URL=http://localhost:8000
# Backend (.env)
DATABASE_URL=postgresql://user:password@localhost:5432/mullet
SECRET_KEY=your-secret-key
DEBUG=True- Run the development servers
# Frontend (in project root)
npm run dev
# Backend (in backend/)
python manage.py runserverVisit http://localhost:3000 to see the app.
- Create a Mode (e.g., "Client Work")
- Add a Goal (e.g., "Launch Product")
- Add a Project under that Goal (e.g., "Website Redesign")
- Add Milestones to the Project (e.g., "Design Phase", "Development Phase")
- Add Tasks to each Milestone
- Navigate to the Timer view
- Select your Mode, Goal, Project, Milestone, or Task
- Click Start to begin timing
- When finished, click Stop (or click Complete if done with the entity)
- View your stats in the Stats view
- Create a project or milestone structure you want to reuse
- Go to Templates view
- Click "Create Template" and define the structure
- Later, click "Use Template" to instantiate it with one click
- TypeScript: Strict mode enabled
- Linting: ESLint with React and TypeScript rules
- Formatting: Prettier (2-space indent, single quotes, trailing commas)
# Run tests
npm test
# Run with coverage
npm run test:coverage# Frontend
npm run build
npm start
# Backend
python manage.py collectstatic
gunicorn config.wsgi:application- Timer controller complexity (~1400 lines) needs refactoring
- Type system has gaps (Task type missing parent ID fields)
- Duplicate Build/Edit form components across entity types
- "All" mode inconsistently supported across views
- Consolidate Build/Edit forms to single components
- Decompose timer controller into focused hooks
- Fix Task type to include goalId, projectId, milestoneId
- Standardize store interfaces (consistent byId vs tasksById)
- Add keyboard shortcuts for power users
- Implement recurring tasks
- Add subtasks
- Mobile app (React Native)
See CONTRIBUTING.md for development guidelines.
This project grew organically and has some areas that need cleanup:
- Timer System:
useTimerControlleris 1440 lines and manages too many concerns - Form Duplication: BuildTaskForm and EditTaskForm are 80% identical (same for other entities)
- Type Safety: 19
as anycasts in timer code due to incomplete Task type - Store Consistency: BatchEditorWindow has defensive type casting due to inconsistent store shapes
- Data Flow: Hybrid pattern of props drilling + direct store access across components
These are documented in detail in the codebase review (see /docs/code-review.md if available).
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please ensure your code:
- Passes TypeScript compilation (
npm run type-check) - Follows the existing code style
- Includes tests for new features
- Updates documentation as needed
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with React and Next.js
- UI components from Radix UI
- Icons from Lucide and Heroicons
- Charts powered by Recharts
- Drag and drop by DnD Kit
Ciaran Kieran β @ciaranjkieran
Project Link: https://github.com/ciaranjkieran/mullet
Note: This is an active project under development. The codebase is functional but has architectural patterns that are being actively refined. See the Tech Debt section for known issues and the Roadmap for planned improvements.