Label Model
The Label model represents organizational labels synced from Plane.so, used for categorizing and filtering work items and flows.
Schema Definition
model Label {
id String @id @default(uuid())
planeId String @unique
name String
color String
description String?
projectId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
@@map("labels")
}
Field Descriptions
Primary Identification
| Field | Type | Description |
|---|---|---|
id | String | UUID primary key |
planeId | String | Unique Plane.so label identifier |
Label Properties
| Field | Type | Description |
|---|---|---|
name | String | Label name (e.g., "bug", "feature", "urgent") |
color | String | Hex color code (e.g., "#FF6B6B", "#4ECDC4") |
description | String? | Optional description of the label's purpose |
Relations
| Field | Type | Description |
|---|---|---|
projectId | String | Reference to Project.id |
Metadata
| Field | Type | Description |
|---|---|---|
createdAt | DateTime | Label sync timestamp |
updatedAt | DateTime | Last update timestamp |
Relationships
Belongs To
- project:
Project- The project this label belongs to (Cascade delete)
Usage Examples
Create Synced Label
const label = await prisma.label.create({
data: {
planeId: "plane-label-123",
name: "bug",
color: "#FF6B6B",
description: "Bug reports and issues",
projectId: "project-456"
}
});
Update Label Properties
await prisma.label.update({
where: { planeId: "plane-label-123" },
data: {
color: "#E74C3C",
description: "Critical bugs and issues"
}
});
Get Labels by Project
const projectLabels = await prisma.label.findMany({
where: { projectId: "project-456" },
orderBy: { name: "asc" }
});
Find Labels by Name
const bugLabels = await prisma.label.findMany({
where: {
name: "bug",
project: {
integration: {
isActive: true
}
}
}
});
Color System
Predefined Color Palette
const LABEL_COLORS = [
'#FF6B6B', // Red - urgent, critical
'#FFA726', // Orange - high priority
'#FFEB3B', // Yellow - medium priority
'#4CAF50', // Green - completed, success
'#2196F3', // Blue - info, default
'#9C27B0', // Purple - feature, enhancement
'#E91E63', // Pink - bug, issue
'#009688', // Teal - testing, qa
'#795548', // Brown - documentation
'#607D8B' // Grey - maintenance
];
Color Assignment Logic
function assignLabelColor(labelName: string): string {
const nameHash = labelName.split('').reduce((hash, char) => {
return ((hash << 5) - hash) + char.charCodeAt(0);
}, 0);
return LABEL_COLORS[Math.abs(nameHash) % LABEL_COLORS.length];
}
Sync Operations
Bulk Label Sync
async function syncProjectLabels(projectId: string, planeLabels: any[]) {
const operations = planeLabels.map(label =>
prisma.label.upsert({
where: { planeId: label.id },
update: {
name: label.name,
color: label.color,
description: label.description,
updatedAt: new Date()
},
create: {
planeId: label.id,
name: label.name,
color: label.color || assignLabelColor(label.name),
description: label.description,
projectId: projectId
}
})
);
await prisma.$transaction(operations);
}
Label Cleanup
async function cleanupOrphanedLabels() {
// Remove labels that no longer exist in Plane
const orphanedLabels = await prisma.label.findMany({
where: {
// Logic to identify orphaned labels
}
});
await prisma.label.deleteMany({
where: {
id: { in: orphanedLabels.map(l => l.id) }
}
});
}
Label Usage Analytics
Most Used Labels
async function getPopularLabels(projectId: string) {
// This would require joining with work items
// Implementation depends on how labels are used in work items
const labelUsage = await prisma.workItem.groupBy({
by: ['labels'],
where: { projectId },
_count: true
});
return labelUsage.sort((a, b) => b._count - a._count);
}
Label Distribution
async function getLabelDistribution(projectId: string) {
const workItems = await prisma.workItem.findMany({
where: { projectId },
select: { labels: true }
});
const labelCounts: Record<string, number> = {};
workItems.forEach(item => {
item.labels.forEach(label => {
labelCounts[label] = (labelCounts[label] || 0) + 1;
});
});
return Object.entries(labelCounts)
.map(([label, count]) => ({ label, count }))
.sort((a, b) => b.count - a.count);
}
Performance Considerations
Indexing
- Unique index on
planeIdfor sync operations - Index on
projectIdfor project filtering - Index on
namefor label name searches - Index on
updatedAtfor sync status
Query Optimization
- Use
selectto fetch only needed fields - Cache frequently used labels
- Use batch operations for bulk syncs
- Implement pagination for large label lists
Best Practices
Naming Conventions
- Use lowercase with hyphens:
bug-fix,feature-request - Keep names descriptive but concise
- Use consistent prefixes:
priority-*,type-*,status-*
Color Consistency
- Use colors consistently across projects
- Choose accessible color combinations
- Consider colorblind users when selecting colors
Maintenance
- Regular cleanup of unused labels
- Update label descriptions as usage evolves
- Audit label usage periodically
- Merge similar labels when appropriate
Integration
- Sync labels regularly from Plane
- Handle label conflicts gracefully
- Preserve local label customizations
- Implement label mapping for migrations