Enterprise-grade, offline-first construction task management with interactive floor plan visualization
A sophisticated construction task management system that enables teams to create, track, and manage tasks directly on interactive floor plans. Built for construction sites with poor connectivity, featuring offline-first architecture, real-time collaboration, and precision spatial data management.
Link to Low-fidelity Wireframes
- Custom Coordinate Systems: Precise architectural coordinate mapping using Leaflet.js CRS
- Multi-Layer Rendering: Task markers, room boundaries, and annotations
- Real-Time Updates: Live task status visualization with color-coded markers
- Click-to-Create: Intuitive task creation at precise floor plan coordinates
- 5-Stage Workflow:
not_started โ in_progress โ blocked โ final_check โ done - Spatial Positioning: Millimeter-accurate task positioning on floor plans
- Hierarchical Checklists: Nested checklist items with progress calculation
- Assignment System: User-based task assignment with role management
- Local Database: RxDB with IndexedDB for complete offline functionality
- Sync Replication: Built-in conflict resolution for multi-user collaboration
- Data Persistence: Automatic state recovery and cache management
- Project Organization: Hierarchical project โ floor plan โ room โ task structure
- Progress Tracking: Real-time completion percentage across all project levels
- Resource Management: Team member assignments and workload distribution
// Complete data flow chain
Component โ Custom Hook โ Zustand Store โ Service Layer โ RxDB โ IndexedDB1. Component Layer - UI components request data through custom hooks
2. Hook Layer - Data fetching with caching, error handling, and store integration
3. Store Layer - Zustand stores for reactive state management and optimistic updates
4. Service Layer - Repository pattern for database operations and API integration
5. Database Layer - RxDB for offline-first, reactive data persistence
src/
โโโ ๐จ components/ # Reusable UI components
โ โโโ ui/ # shadcn/ui design system
โ โโโ ... # Business-specific components
โโโ ๐ฆ containers/ # Page-level container components
โ โโโ login/ # Authentication interface
โ โโโ project/ # Project management UI
โ โโโ projectDetails/ # Main workspace
โ โโโ floorPlanView/ # Floor plan visualization
โ โ โโโ floorPlanArea/ # Leaflet map integration
โ โ โโโ tasksList/ # Task management panels
โ โ โโโ taskDetails/ # Task detail forms
โ โโโ task/ # Task CRUD operations
โโโ ๐๏ธ database/ # Offline-first database layer
โ โโโ schemas/ # RxDB schema definitions
โ โโโ services/ # Repository pattern implementations
โ โ โโโ base.service.ts # Common database operations
โ โ โโโ project.service.ts # Project CRUD + statistics
โ โ โโโ task.service.ts # Task management + checklist
โ โ โโโ index.ts # Service aggregation
โ โโโ dtos/ # Data transfer objects
โ โโโ errors/ # Centralized error handling
โโโ ๐ฃ hooks/ # Custom React hooks for data fetching
โโโ ๐ช stores/ # Zustand state management
โโโ ๐ services/ # API integration layer
โ โโโ apiHelper.ts # Online/offline request handler
โ โโโ project.ts # Project API service example
โ โโโ task.ts # Task API operations
โ โโโ api.ts # Axios configuration
โโโ ๐ pages/ # Route-level components
โโโ ๐งญ router/ # React Router configuration
โโโ ๐ ๏ธ utils/ # Utility functions
โโโ ๐ locales/ # Internationalization
- Node.js
^22.17.0(LTS) - npm or yarn
# Clone the repository
git clone https://github.com/your-username/construction-task-manager.git
cd construction-task-manager
# Install dependencies
npm install
# Start development server
npm run devCreate a .env file:
VITE_API_BASE_URL=http://localhost:3001/api
VITE_OFFLINE_MODE=falseThe application uses a relational schema optimized for construction workflows:
// Core entity relationships
Projects (1:N) โ FloorPlans (1:N) โ Rooms (1:N) โ Tasks (1:N) โ ChecklistItems
โ
Users (Assignments)Key Schema Features:
- Spatial Data: Precise coordinate storage for floor plan positioning
- Audit Trails: Complete change tracking with timestamps and user attribution
- Status Workflows: Finite state machines for task progression
- Progress Calculation: Automatic completion percentage aggregation
The floor plan visualization system uses Leaflet.js with a custom coordinate system to transform architectural drawings into interactive task management interfaces.
Each floor plan contains detailed spatial and scale information:
interface FloorPlan {
// Identity
id: string;
name: string; // e.g., "Ground Floor"
project_id: string;
// Image Properties
image_url: string; // Path to architectural drawing
image_width: number; // Pixel width (e.g., 1536px)
image_height: number; // Pixel height (e.g., 1024px)
// Scale Calibration
scale_pixels_per_meter: number; // Conversion factor (e.g., 40px = 1m)
floor_level: number; // Building level (0 = ground floor)
}Key Attributes Explained:
scale_pixels_per_meter: Critical for accurate measurements - converts screen pixels to real-world metersimage_width/height: Defines the coordinate system bounds for precise positioningfloor_level: Enables multi-story building support with vertical organization
Rooms are defined by polygon coordinates that overlay perfectly on floor plans:
interface Room {
id: string;
name: string; // e.g., "Storage Unit", "Conference Room"
floor_plan_id: string;
boundary_coordinates: string; // JSON array: "[[x1,y1], [x2,y2], ...]"
room_type: RoomType; // LIVING_ROOM, OFFICE, STORAGE, etc.
area_sqm?: number; // Calculated or manual area
}Coordinate System:
// Example room boundary (rectangular storage unit)
const storageUnit = {
boundary_coordinates: '[[125, 800], [590, 800], [590, 1336], [125, 1336]]'
// Creates a 465px ร 536px rectangle
// Real-world size: ~11.6m ร 13.4m (with 40px/meter scale)
};Tasks are positioned using Leaflet's coordinate system:
interface Task {
// ... other properties
position_lat: number; // Y-coordinate (-10000 to 10000)
position_lng: number; // X-coordinate (-10000 to 10000)
room_id: string; // Associated room for context
}Coordinate Bounds:
- Range:
-10000to10000(architectural coordinates, not geographic) - Resolution: Sub-pixel precision for millimeter-accurate positioning
- Origin: Top-left corner of floor plan image
// LeafletFloorPlan.tsx - Multi-layer rendering
// 1. Base Image Layer
const imageOverlay = L.imageOverlay(
floorPlan.image_url,
[[0, 0], [floorPlan.image_height, floorPlan.image_width]]
).addTo(map);
// 2. Room Boundary Layer
rooms.forEach((room) => {
const coordinates = JSON.parse(room.boundary_coordinates);
L.polygon(coordinates, {
color: '#3b82f6',
fillOpacity: 0.1,
weight: 2
}).addTo(map);
});
// 3. Task Marker Layer
tasks.forEach((task) => {
L.marker([task.position_lat, task.position_lng], {
icon: createTaskIcon(task.status)
}).addTo(map);
});The system uses curated architectural drawings instead of user uploads:
export const PREDEFINED_FLOOR_PLANS = [
{
id: 'plan-a',
name: 'Small Office',
image_url: '/assets/plans/1.png',
image_width: 1536,
image_height: 1024,
scale_pixels_per_meter: 40,
description: '20m x 15m office building',
rooms: [{
name: 'Storage Unit',
boundary_coordinates: '[[125, 800], [590, 800], [590, 1336], [125, 1336]]'
}]
}
];Why Predefined Images?
- Quality Control: Professional architectural drawings with known scales
- Immediate Usability: No need for users to calibrate or upload plans
- Consistent Experience: Standardized room layouts and proportions
- Demo Readiness: Instant project creation with realistic floor plans
// project.service.ts - Auto-setup on project creation
private async createDefaultFloorPlanAndRoom(projectId: string): Promise<void> {
const defaultPlan = PREDEFINED_FLOOR_PLANS[0];
// Create floor plan with predefined dimensions
const floorPlan = await this.db.floor_plans.insert({
name: 'Ground Floor',
project_id: projectId,
image_url: defaultPlan.image_url,
image_width: defaultPlan.image_width,
image_height: defaultPlan.image_height,
scale_pixels_per_meter: defaultPlan.scale_pixels_per_meter,
});
// Create default room with predefined boundaries
await this.db.rooms.insert({
name: 'Storage Unit',
floor_plan_id: floorPlan.id,
boundary_coordinates: defaultPlan.rooms[0].boundary_coordinates,
room_type: RoomType.LIVING_ROOM,
});
}Benefits:
- Zero Configuration: Projects immediately have interactive floor plans
- Consistent Scale: All predefined plans use verified pixel-to-meter ratios
- Room Context: Tasks are automatically associated with logical spaces
// Zustand store pattern
interface TaskStore {
// State
tasks: TaskWithDetails[];
selectedTask: TaskWithDetails | null;
lastFetched: number | null;
// Actions
setTasks: (tasks: TaskWithDetails[]) => void;
addTask: (task: TaskWithDetails) => void;
updateTask: (id: string, updates: Partial<TaskWithDetails>) => void;
deleteTask: (id: string) => void;
// Computation utils functions
calculateChecklistCounts: (checklistItems?: ChecklistItem[]) => number;
}The application uses a dual-mode architecture handling both online API calls and offline database operations:
// apiHelper.ts - Smart request routing
const withApi = async <T>(
apiCallback: (api: AxiosInstance) => Promise<{ data: T }>,
fallbackFn: (db: DatabaseServices) => Promise<T>
): Promise<T> => {
if (import.meta.env.VITE_OFFLINE_MODE === 'true') {
const db = await ensureDatabaseInitialized();
return await fallbackFn(db);
}
// Online mode: API first, fallback to database on network error
try {
const response = await apiCallback(api);
return response.data;
} catch (error) {
if (isNetworkError(error) && fallbackFn) {
const db = await ensureDatabaseInitialized();
return await fallbackFn(db);
}
throw formatApiErrorResponse(error); // formatted error
}
};Service Implementation Example:
// services/project.ts - Dual-mode service
export const fetchProjectsByUser = async (userId: string): Promise<ProjectWithStats[]> => {
return await withApi(
// Online: REST API call
(api) => api.get(`/projects/user/${userId}`),
// Offline: Database service call
async (db) => {
return await db.projects.findProjectsByUserWithStats(userId);
}
);
};
export const createProject = async (payload: CreateProjectDto): Promise<Project> => {
return await withApi(
(api) => api.post('/projects', payload),
async (db) => {
const projectDoc = await db.projects.createProject(payload);
return projectDoc.toJSON() as Project;
}
);
};Benefits:
- Environment Flexibility: Switch between online/offline modes via environment variables
- Graceful Degradation: Automatic fallback to local database on network failures
- Consistent Interface: Services remain identical regardless of data source
- Development Speed: Rapid prototyping with offline-first approach
# E2E tests (Playwright)
npm run test:e2e # Run all E2E tests
npm run test:e2e:ui # Run with Playwright UI
npm run test:e2e:headed # Run in headed mode (browser visible)
npm run test:e2e:debug # Debug mode with inspector
npm run test:e2e:report # View test report
# Run specific test files
npm run test:e2e -- e2e/tests/login.spec.ts
npm run test:e2e -- e2e/tests/home.spec.ts
npm run test:e2e -- e2e/tests/projectDetails/floorplan.spec.ts
# Unit tests (Future implementation)
npm run test # Run unit tests
npm run test:coverage # Generate coverage report| Test Suite | Status | Coverage | Notes |
|---|---|---|---|
| Login Flow | โ Passing | 100% | Complete user authentication workflow |
| Home Page | โ Passing | 90% | Project listing, creation, navigation |
| Floor Plan View | 60% | Basic layout tests, simplified due to complexity | |
| Kanban View | โ Skipped | 0% | Blocked by UI element conflicts (see tech debt) |
| Unit Tests | โ Not Implemented | 0% | Time constraint - see tech debt section |
โ ๏ธ Due to time constraints, several testing areas need improvement in production:
// โ Current: Generic selectors without data-testid
await page.click('button:has-text("Create Project")');
await page.click('.group.cursor-pointer');
// โ
Production: Proper test selectors
await page.click('[data-testid="create-project-button"]');
await page.click('[data-testid="project-card"]');Problems:
- Brittle Selectors: Tests break when CSS classes or text content changes
- Poor Maintainability: Difficult to update when UI evolves
- Flaky Tests: Generic selectors may match multiple elements
# โ Current: No unit tests implemented
src/
โโโ components/ # 0% test coverage
โโโ hooks/ # 0% test coverage
โโโ services/ # 0% test coverage
โโโ stores/ # 0% test coverage
โโโ utils/ # 0% test coverage
# โ
Production: Comprehensive unit testing needed
src/
โโโ __tests__/
โ โโโ components/
โ โโโ hooks/
โ โโโ services/
โ โโโ utils/Critical Missing Tests:
- Custom Hooks: Data fetching, state management, error handling
- Database Services: CRUD operations, complex queries, error scenarios
- Utility Functions: Coordinate transformations, validation, calculations
- Component Logic: User interactions, state updates, error boundaries
// Missing: Database service integration tests
describe('TaskService Integration', () => {
test('should handle complex task queries with spatial filtering', async () => {
// Test room boundary filtering
// Test coordinate range queries
// Test task status aggregations
});
});
// Missing: API service integration tests
describe('API Integration', () => {
test('should gracefully handle network failures', async () => {
// Test offline fallback behavior
// Test sync conflict resolution
// Test data consistency
});
});- Add data-testid attributes throughout the application
- Fix kanban UI conflicts - implement proper toast dismissal
- Enhance floor plan tests - add interaction and task creation tests
- Improve test reliability - add proper waits and error handling
- Setup Vitest configuration with proper coverage thresholds
- Component testing with React Testing Library
- Custom hook testing with renderHook utilities
- Service layer testing with database mocking
- Utility function testing with edge case coverage
- Database service integration tests with real RxDB instances
- API integration tests with MSW (Mock Service Worker)
- Cross-browser testing expansion
- Performance testing with Lighthouse CI
- Visual regression testing with Playwright screenshots
- Accessibility testing with axe-playwright
- Mobile responsive testing across device viewports
- Performance benchmarking with load testing with Playwright simulating rapid UI actions programmatically
Estimated Total: 9-13 days for production-ready test coverage
// Target test coverage thresholds
export const coverageThresholds = {
global: {
branches: 85,
functions: 85,
lines: 85,
statements: 85
},
// Critical business logic requires higher coverage
'src/services/': {
branches: 95,
functions: 95,
lines: 95,
statements: 95
}
};npm run build:analyze- Code Splitting: Route-based and component-based lazy loading
- Tree Shaking: Aggressive dead code elimination
- Asset Optimization: Image compression and format optimization
- Memory Management: Efficient RxDB query optimization and cleanup
- First Contentful Paint: < 1.5s
- Time to Interactive: < 3s
- Bundle Size: < 500KB gzipped
// Multi-language support
const supportedLanguages = ['en', 'de'];- Data Isolation: Per-user database instances
- Input Validation: Zod schema validation at all boundaries
- Audit Logging: Complete change tracking for compliance
- WebSocket integration for live updates
- Conflict resolution UI for simultaneous edits
- User presence indicators on floor plans
- Measurement tools (distance, area, angle)
- CAD file import/export capabilities
- 3D visualization with Three.js (Multi floors)
- REST API for external tool integration
- Single Sign-On (SSO/OAuth)
- Advanced reporting and analytics dashboard
- React Native mobile application
- Offline synchronization between devices
- Camera integration for progress photos
- Fork the repository
- Create a feature branch (
git checkout -b ft/amazing-feature) - Commit changes (
git commit -m 'feat: Add amazing feature') - Push to branch (
git push origin ft/amazing-feature) - Open a Pull Request
- Project Setup & Architecture (3 hours) - React + TypeScript + Vite + RxDB + Leaflet + Documentation of various solutions(options out there)
- Database Schema Design (~1 hours) - Entity relationships, spatial data modeling
- Authentication System (~1 hours) - Simple login with data isolation
- Basic UI Components (7 hours) - shadcn/ui integration + Static pages
- Project Management CRUD (3 hours) - Create, read, update, delete projects
- Floor Plan Management (~1 hours) - Upload and basic floor plan display
- Task Management System (5 hours) - Full CRUD with status workflow
- Interactive Floor Plan Visualization (4 hours) - Leaflet integration, custom CRS, markers
- Room Management (3 hours) - Room boundaries and spatial organization
- Task-Floor Plan Integration (2 hours) - Click-to-create, coordinate mapping
- State Management (3 hours) - Zustand stores, optimistic updates
- Offline-First Implementation (~6 hours) - RxDB services, sync preparation
- UI/UX Refinement (~1 hour) - Responsive design, loading states, error handling
- Internationalization (~1 hours) - Multi-language support setup
- Performance Optimization (~1 hours) - Bundle analysis, lazy loading
- Documentation (2 hours) - Comprehensive README, technical documentation
Total Development Time: ~44 hours (5.5 days)
Due to limited time, here are the key areas I would improve in a production environment:
-
Testing Infrastructure (High Priority)
// โ Current: Minimal test coverage with brittle selectors test('should create project', async () => { await page.click('button:has-text("Create Project")'); // Brittle await page.fill('input[name="name"]', 'Test Project'); // Generic }); // โ Better: Comprehensive testing with proper selectors test('should create project', async () => { await page.click('[data-testid="create-project-button"]'); await page.fill('[data-testid="project-name-input"]', 'Test Project'); await expect(page.locator('[data-testid="project-card"]')).toBeVisible(); });
Critical Issues:
- E2E Tests: Kanban tests completely skipped due to UI conflicts
- Unit Tests: 0% coverage across entire codebase
- Brittle Selectors: Tests break on CSS/content changes
- No Integration Tests: Database and API layers untested
Impact: Production deployments without proper test validation
-
Component Optimization
// Current: Multiple useEffect hooks in LeafletFloorPlan // Better: Custom hook for map lifecycle management const useLeafletMap = (floorPlan, onTaskCreate) => { // Consolidated map setup, cleanup, and event handling };
-
Floor Plan System Limitations
// Current: Fixed predefined floor plans only export const PREDEFINED_FLOOR_PLANS = [ { id: 'plan-a', image_url: '/assets/plans/1.png' }, { id: 'plan-b', image_url: '/assets/plans/2.png' } ]; // Missing: Custom floor plan upload and calibration
Limitations:
- No custom floor plan upload functionality
- Manual coordinate calibration required for new plans
- Limited to 3 predefined architectural layouts
- No support for CAD file import (DWG, DXF)
-
Data Aggregation Performance Issues
// Current: Inefficient multiple queries for statistics async findProjectsByUserWithStats(projectId: string): Promise<ProjectWithStats> { const project = await this.findById(projectId); const floorPlans = await this.db.floorPlans.find({ selector: { project_id: projectId } }).exec(); // Multiple sequential queries - inefficient for (const floorPlan of floorPlans) { const rooms = await this.db.rooms.find({ selector: { floor_plan_id: floorPlan.id } }).exec(); for (const room of rooms) { const tasks = await this.db.tasks.find({ selector: { room_id: room.id } }).exec(); // Calculate stats individually... } } } // Current workaround: Client-side aggregation with performance cost
Impact: N+1 query problems causing 50-100ms delays for complex project statistics
-
Coordinate System Edge Cases
// Current: Basic coordinate validation position_lat: { minimum: -10000, maximum: 10000 } // Missing: Room boundary validation, coordinate transformation edge cases
Issues:
- Tasks can be positioned outside room boundaries
- No validation for overlapping rooms
- Coordinate system doesn't handle rotation or skewed floor plans
-
Performance Optimizations
- Implement React.memo for expensive components
- Optimize RxDB queries with proper indexing
- compress RxDB database, use compression
- Advanced code splitting & lazy loading of some libraries & functionalities
-
Accessibility (WCAG 2.1 AA)
- Keyboard navigation for floor plan interactions
- Screen readers
- Focus management in complex modals
-
Floor Plan Enhancement Needs
- Custom Upload System: File upload with automatic scale detection
- Advanced Measurement Tools: Distance, area, and angle measurement
- Room Creation UI: Interactive polygon drawing for custom rooms
- Multi-Floor Support: Vertical navigation between building levels
- Coordinate Precision: Sub-pixel accuracy for construction-grade measurements
-
Real-time Collaboration
- WebSocket integration for live updates
- Conflict resolution UI for simultaneous edits
- User presence indicators on floor plans
-
Production Infrastructure
- Docker containerization
- CI/CD pipeline with GitHub Actions
- Performance monitoring and analytics
| Approach | Performance | Scalability | Precision | Development Time | Verdict |
|---|---|---|---|---|---|
| Generic Task Apps | Good for simple lists | Limited spatial context | No coordinate mapping | Fast | โ Wrong domain |
| CAD Software (AutoCAD Web) | Excellent precision | Enterprise scale | Professional grade | Very slow | โ Overkill complexity |
| Static Image + SVG Overlay | Poor with 100+ elements | Limited by DOM manipulation | Pixel-level only | Fast | โ Not scalable |
| Static Image + Absolute Positioned Markers | Degrades with complex interactions | Poor memory management | Basic pixel positioning | Very fast | โ No real coordinates |
| HTML5 Canvas (Custom) | Excellent with hardware acceleration | Handles thousands efficiently | Pixel-perfect control | Very slow | |
| React-Konva | Very good React integration | Good with layer management | Sub-pixel precision | Medium | โ Strong contender |
| FabricJS | Excellent interactive graphics | Good object management | High precision vector graphics | Medium | โ Strong contender |
| Paper.js | Excellent vector animations | Good for illustrations | Mathematical vector precision | Medium | |
| PixiJS (Game Engine) | Exceptional WebGL performance | Excellent with GPU acceleration | High precision coordinates | Slow | |
| LeafletJS + Canvas Overlay | Optimized for mapping data | Enterprise GIS scale | Professional coordinate systems | Medium-Slow | โ Selected |
| OpenLayers | Superior GIS performance | Handles massive datasets | Advanced CRS support | Slow | |
| D3.js + SVG | Good for data visualization | Limited by SVG performance | Vector precision | Medium |
Professional foundation: LeafletJS handles coordinate systems, zoom/pan, and layer management - exactly what construction mapping needs.
Performance at scale: Canvas overlays provide hardware-accelerated rendering for drawing tools while maintaining 60fps with hundreds of tasks.
Real-world precision: Custom CRS (Coordinate Reference System) enables millimeter-accurate positioning and measurements.
Offline-ready: Tile caching and offline-first architecture work seamlessly with construction site realities.
Built with โค๏ธ for construction teams worldwide

