Workshop: SaaS MVP (4 hours) β
Commercial License Required
This workshop uses MakerKit, which requires a commercial license. While Orchestre can help you build with it, you must obtain a valid license from MakerKit for commercial use.
Build a complete SaaS minimum viable product with user authentication, team management, subscription billing, and a functional application. Perfect for entrepreneurs and developers looking to launch quickly.
Workshop Overview β
Duration: 4 hours Difficulty: Intermediate-Advanced Result: Launch-ready SaaS application
You'll build TeamTask - a project management SaaS with:
- π User authentication with team invites
- π³ Subscription billing (Free/Pro/Enterprise)
- π Kanban board for task management
- π₯ Team collaboration features
- π Analytics dashboard
- π Production deployment
Prerequisites β
- Orchestre installed
- Stripe account (free test mode)
- Basic understanding of SaaS concepts
- 4 hours of focused time
Part 1: Foundation (45 minutes) β
Project Setup β
/orchestre:create (MCP) teamtask makerkit-nextjs
cd teamtaskStrategic Planning β
/orchestre:orchestrate (MCP) "Build a project management SaaS MVP with:
- Kanban boards for visual task management
- Team workspaces with role-based permissions
- Three pricing tiers: Free (1 project), Pro ($29/mo, 10 projects), Enterprise ($99/mo, unlimited)
- Real-time collaboration
- Activity tracking and notifications
- API for integrations"Environment Configuration β
# Setup environment
cp .env.example .env.local
# Configure essential services
/setup-stripe
/setup-oauth "Google and GitHub providers"Update .env.local:
# Database (Supabase recommended for quick start)
DATABASE_URL=postgresql://...
# Stripe
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
# OAuth
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...Part 2: Core Data Model (30 minutes) β
Design the Schema β
/orchestre:execute-task (MCP) "Create data model for projects, boards, tasks, and team members with proper relationships"Core models created:
model Organization {
id String @id @default(cuid())
name String
slug String @unique
subscription Subscription?
projects Project[]
members Member[]
invitations Invitation[]
createdAt DateTime @default(now())
}
model Project {
id String @id @default(cuid())
name String
description String?
organizationId String
organization Organization @relation(...)
boards Board[]
activities Activity[]
createdAt DateTime @default(now())
}
model Board {
id String @id @default(cuid())
name String
position Int
projectId String
project Project @relation(...)
tasks Task[]
}
model Task {
id String @id @default(cuid())
title String
description String?
position Int
boardId String
board Board @relation(...)
assigneeId String?
assignee User? @relation(...)
dueDate DateTime?
priority Priority @default(MEDIUM)
labels Label[]
comments Comment[]
activities Activity[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum Priority {
LOW
MEDIUM
HIGH
URGENT
}Run Migrations β
npm run db:migrate
npm run db:seed # Optional: seed with demo dataPart 3: Subscription System (45 minutes) β
Configure Pricing Tiers β
/add-subscription-plan "Free tier: 1 project, 2 members, basic features"
/add-subscription-plan "Pro tier: 10 projects, 10 members, advanced features, $29/month"
/add-subscription-plan "Enterprise tier: Unlimited everything, priority support, $99/month"Implement Feature Gates β
/execute-task "Add subscription-based feature gates and limits enforcement"Feature gating example:
// lib/subscription-limits.ts
export const PLAN_LIMITS = {
free: {
projects: 1,
members: 2,
storage: 100, // MB
features: ['basic_boards', 'basic_tasks']
},
pro: {
projects: 10,
members: 10,
storage: 10000, // MB
features: ['basic_boards', 'basic_tasks', 'advanced_analytics', 'api_access', 'integrations']
},
enterprise: {
projects: -1, // unlimited
members: -1,
storage: -1,
features: ['all']
}
}
// Enforcement hook
export function useFeatureGate(feature: string) {
const { subscription } = useSubscription()
const plan = PLAN_LIMITS[subscription.plan]
return {
hasAccess: plan.features.includes(feature) || plan.features.includes('all'),
limit: plan,
showUpgrade: !plan.features.includes(feature)
}
}Billing Portal β
/execute-task "Create billing portal with subscription management"Part 4: Core Application (1 hour) β
Kanban Board β
/execute-task "Create drag-and-drop Kanban board with real-time updates"Key implementation:
// components/KanbanBoard.tsx
'use client'
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
import { useOptimistic } from 'react'
export function KanbanBoard({ project }) {
const [boards, setBoards] = useOptimistic(project.boards)
const handleDragEnd = async (result) => {
if (!result.destination) return
// Optimistic update
const newBoards = reorderTasks(boards, result)
setBoards(newBoards)
// Persist to database
await updateTaskPosition({
taskId: result.draggableId,
boardId: result.destination.droppableId,
position: result.destination.index
})
}
return (
<DragDropContext onDragEnd={handleDragEnd}>
<div className="kanban-container">
{boards.map(board => (
<Droppable key={board.id} droppableId={board.id}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className="board-column"
>
<h3>{board.name}</h3>
{board.tasks.map((task, index) => (
<Draggable
key={task.id}
draggableId={task.id}
index={index}
>
{(provided) => (
<TaskCard
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
task={task}
/>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
))}
</div>
</DragDropContext>
)
}Task Management β
/execute-task "Implement task creation, editing, and assignment with notifications"Team Features β
/execute-task "Create member invitation system with email notifications"
/execute-task "Implement role-based permissions (Owner, Admin, Member)"Part 5: Real-time Collaboration (45 minutes) β
WebSocket Setup β
/execute-task "Implement WebSocket server for real-time updates"Live Cursors β
/execute-task "Add live cursor tracking for collaborative editing"Implementation:
// hooks/useCollaboration.ts
export function useCollaboration(projectId: string) {
const [collaborators, setCollaborators] = useState<Collaborator[]>([])
const ws = useRef<WebSocket>()
useEffect(() => {
ws.current = new WebSocket(`${WS_URL}/project/${projectId}`)
ws.current.on('user:joined', (user) => {
setCollaborators(prev => [...prev, user])
})
ws.current.on('cursor:move', ({ userId, position }) => {
setCollaborators(prev =>
prev.map(c => c.id === userId
? { ...c, cursor: position }
: c
)
)
})
ws.current.on('task:update', (task) => {
// Update local state
updateTask(task)
})
return () => ws.current?.close()
}, [projectId])
const broadcastCursor = (position: Position) => {
ws.current?.send('cursor:move', position)
}
return { collaborators, broadcastCursor }
}Activity Feed β
/execute-task "Create real-time activity feed with notifications"Part 6: Analytics & Insights (30 minutes) β
Analytics Dashboard β
/execute-task "Build analytics dashboard showing project metrics and team productivity"Dashboard components:
// components/Analytics.tsx
export function AnalyticsDashboard({ organizationId }) {
const { data: metrics } = useMetrics(organizationId)
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<MetricCard
title="Active Projects"
value={metrics.activeProjects}
change={metrics.projectsChange}
icon={<ProjectIcon />}
/>
<MetricCard
title="Tasks Completed"
value={metrics.tasksCompleted}
change={metrics.tasksChange}
period="This week"
/>
<MetricCard
title="Team Velocity"
value={`${metrics.velocity} tasks/week`}
chart={<VelocityChart data={metrics.velocityHistory} />}
/>
<MetricCard
title="Avg Resolution Time"
value={`${metrics.avgResolutionTime}h`}
change={metrics.resolutionChange}
/>
</div>
)
}Reports β
/execute-task "Generate weekly team productivity reports"Part 7: API & Integrations (30 minutes) β
REST API β
/add-api-endpoint "GET /api/v1/projects"
/add-api-endpoint "POST /api/v1/tasks"
/add-api-endpoint "PUT /api/v1/tasks/:id"
/add-api-endpoint "POST /api/v1/webhooks"API Authentication β
/execute-task "Implement API key authentication for external integrations"Webhook System β
/add-webhook "Task status changes"
/add-webhook "New team member added"
/add-webhook "Project completed"Part 8: Launch Preparation (30 minutes) β
Pre-launch Checklist β
/validate-implementation "SaaS MVP production readiness"Checklist includes:
- [ ] Authentication working
- [ ] Billing integration tested
- [ ] Email notifications configured
- [ ] Error tracking setup
- [ ] Performance optimized
- [ ] Security audit passed
- [ ] Legal pages added
- [ ] Analytics configured
Add Essential Pages β
/execute-task "Create landing page with pricing"
/execute-task "Add terms of service and privacy policy"
/execute-task "Create help documentation"Production Deployment β
/deploy-productionTesting Your SaaS β
User Journey Test β
Sign Up Flow:
- Register new account
- Create organization
- Choose plan
- Complete payment
Team Setup:
- Invite team members
- Set permissions
- Create first project
Core Features:
- Create boards
- Add tasks
- Drag between columns
- Assign to team
- Track progress
Subscription:
- Upgrade plan
- Add payment method
- Download invoice
Complete Implementation Examples β
1. Subscription Enforcement β
// middleware/subscription.ts
export async function enforceSubscriptionLimits(
req: Request,
{ params }: { params: { organizationId: string } }
) {
const { subscription, usage } = await getSubscriptionAndUsage(params.organizationId)
const limits = PLAN_LIMITS[subscription.plan]
// Check project limit
if (req.method === 'POST' && req.url.includes('/projects')) {
if (limits.projects !== -1 && usage.projects >= limits.projects) {
return NextResponse.json({
error: 'Project limit reached',
limit: limits.projects,
upgrade_url: '/settings/billing'
}, { status: 403 })
}
}
// Check member limit
if (req.method === 'POST' && req.url.includes('/invitations')) {
if (limits.members !== -1 && usage.members >= limits.members) {
return NextResponse.json({
error: 'Member limit reached',
limit: limits.members,
upgrade_url: '/settings/billing'
}, { status: 403 })
}
}
return NextResponse.next()
}2. Real-time Sync β
// services/realtime.ts
export class RealtimeService {
private io: Server
broadcastTaskUpdate(task: Task, projectId: string) {
this.io.to(`project:${projectId}`).emit('task:update', {
type: 'UPDATE',
task,
timestamp: Date.now()
})
}
broadcastBoardReorder(
projectId: string,
boardId: string,
tasks: Task[]
) {
this.io.to(`project:${projectId}`).emit('board:reorder', {
boardId,
tasks,
timestamp: Date.now()
})
}
notifyUserActivity(
projectId: string,
userId: string,
activity: Activity
) {
this.io.to(`project:${projectId}`).emit('user:activity', {
userId,
activity,
timestamp: Date.now()
})
}
}3. Analytics Tracking β
// lib/analytics.ts
export class Analytics {
async trackTaskCompleted(task: Task, userId: string) {
const timeToComplete = Date.now() - task.createdAt.getTime()
await this.events.track({
event: 'task_completed',
userId,
properties: {
taskId: task.id,
projectId: task.board.projectId,
timeToComplete,
priority: task.priority,
hadDueDate: !!task.dueDate,
wasOverdue: task.dueDate ? task.dueDate < new Date() : false
}
})
// Update team metrics
await this.updateTeamVelocity(task.board.projectId)
}
async generateWeeklyReport(organizationId: string) {
const metrics = await this.calculateMetrics(organizationId, 'week')
return {
tasksCompleted: metrics.completed,
tasksCreated: metrics.created,
velocity: metrics.velocity,
topPerformers: metrics.topPerformers,
bottlenecks: metrics.bottlenecks,
recommendations: this.generateRecommendations(metrics)
}
}
}Monetization Strategies β
1. Freemium Model β
- Free tier to attract users
- Clear upgrade prompts
- Value-based pricing
2. Usage-Based Pricing β
/execute-task "Add usage-based pricing for API calls"3. Enterprise Features β
/add-enterprise-feature "SSO authentication"
/add-enterprise-feature "Advanced security controls"
/add-enterprise-feature "Custom integrations"Marketing & Growth β
1. SEO Optimization β
/execute-task "Optimize landing page for search engines"2. Product Hunt Launch β
/execute-task "Prepare Product Hunt launch materials"3. Content Marketing β
- Start a blog
- Create tutorials
- Build in public
Performance Metrics β
Target metrics:
- Page Load: <2s
- Time to Interactive: <3s
- API Response: <200ms
- WebSocket Latency: <50ms
- Uptime: 99.9%
Scaling Considerations β
As you grow:
- Database: Consider read replicas
- Caching: Implement Redis
- CDN: Use for global distribution
- Queue: Add job processing
- Monitoring: Enhanced observability
What You've Built β
β Complete SaaS MVP β Subscription billing β Team collaboration β Real-time features β Production deployed β Ready to scale
Next Steps β
Your SaaS is ready to launch! Consider:
- Beta Users: Get 10 beta users for feedback
- Marketing Site: Enhance landing page
- Content: Create onboarding videos
- Support: Set up customer support
- Analytics: Track user behavior
- Iterate: Ship improvements weekly
Resources β
Congratulations! You've built a production-ready SaaS in 4 hours. Now go find your first customers!
