@allpepper/task-orchestrator-tui 1.2.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/tui/components/status-actions.tsx +59 -21
- package/src/tui/screens/dashboard.tsx +9 -69
- package/src/tui/screens/feature-detail.tsx +77 -49
- package/src/tui/screens/project-detail.tsx +8 -65
- package/src/tui/screens/project-view.tsx +76 -80
- package/src/tui/screens/task-detail.tsx +46 -19
- package/src/ui/adapters/direct.ts +323 -94
- package/src/ui/adapters/types.ts +75 -72
- package/src/ui/hooks/use-data.ts +106 -193
- package/src/ui/hooks/use-feature-kanban.ts +45 -30
- package/src/ui/hooks/use-kanban.ts +35 -27
- package/src/ui/index.ts +1 -0
- package/src/ui/lib/colors.ts +27 -24
- package/src/ui/lib/markdown.tsx +5 -5
- package/src/ui/lib/types.ts +0 -1
- package/src/ui/themes/dark.ts +10 -28
- package/src/ui/themes/light.ts +16 -34
- package/src/ui/themes/types.ts +14 -26
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
2
2
|
import { useAdapter } from '../context/adapter-context';
|
|
3
|
-
import type { Feature,
|
|
3
|
+
import type { Feature, Task } from '@allpepper/task-orchestrator';
|
|
4
4
|
import type { BoardFeature, FeatureBoardColumn } from '../lib/types';
|
|
5
|
+
import { isCompletedStatus } from '../lib/colors';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
* Feature-status columns for the feature-based Kanban board
|
|
8
|
+
* v2 Feature-status columns for the feature-based Kanban board
|
|
9
|
+
* Features: NEW → ACTIVE → READY_TO_PROD → CLOSED (+ WILL_NOT_IMPLEMENT)
|
|
8
10
|
*/
|
|
9
11
|
export const FEATURE_KANBAN_STATUSES = [
|
|
10
|
-
{ id: '
|
|
11
|
-
{ id: '
|
|
12
|
-
{ id: '
|
|
13
|
-
{ id: '
|
|
14
|
-
{ id: '
|
|
15
|
-
{ id: 'pending-review', title: 'Pending Review', status: 'PENDING_REVIEW' },
|
|
16
|
-
{ id: 'blocked', title: 'Blocked', status: 'BLOCKED' },
|
|
17
|
-
{ id: 'on-hold', title: 'On Hold', status: 'ON_HOLD' },
|
|
18
|
-
{ id: 'deployed', title: 'Deployed', status: 'DEPLOYED' },
|
|
19
|
-
{ id: 'completed', title: 'Completed', status: 'COMPLETED' },
|
|
20
|
-
{ id: 'archived', title: 'Archived', status: 'ARCHIVED' },
|
|
12
|
+
{ id: 'new', title: 'New', status: 'NEW' },
|
|
13
|
+
{ id: 'active', title: 'Active', status: 'ACTIVE' },
|
|
14
|
+
{ id: 'ready-to-prod', title: 'Ready to Prod', status: 'READY_TO_PROD' },
|
|
15
|
+
{ id: 'closed', title: 'Closed', status: 'CLOSED' },
|
|
16
|
+
{ id: 'will-not-implement', title: 'Will Not Implement', status: 'WILL_NOT_IMPLEMENT' },
|
|
21
17
|
] as const;
|
|
22
18
|
|
|
23
19
|
interface UseFeatureKanbanReturn {
|
|
@@ -30,14 +26,12 @@ interface UseFeatureKanbanReturn {
|
|
|
30
26
|
|
|
31
27
|
/**
|
|
32
28
|
* Hook for managing the feature-based Kanban board.
|
|
33
|
-
*
|
|
34
|
-
* Fetches features and tasks for a project, groups features by their status
|
|
35
|
-
* into columns, and nests tasks within each feature card.
|
|
29
|
+
* In v2, features move through the pipeline via advance/revert.
|
|
36
30
|
*/
|
|
37
31
|
export function useFeatureKanban(projectId: string): UseFeatureKanbanReturn {
|
|
38
32
|
const { adapter } = useAdapter();
|
|
39
33
|
const [features, setFeatures] = useState<Feature[]>([]);
|
|
40
|
-
const [tasksByFeature, setTasksByFeature] = useState<Map<string,
|
|
34
|
+
const [tasksByFeature, setTasksByFeature] = useState<Map<string, Task[]>>(new Map());
|
|
41
35
|
const [loading, setLoading] = useState(true);
|
|
42
36
|
const [error, setError] = useState<string | null>(null);
|
|
43
37
|
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
|
@@ -69,8 +63,7 @@ export function useFeatureKanban(projectId: string): UseFeatureKanbanReturn {
|
|
|
69
63
|
|
|
70
64
|
setFeatures(featuresResult.data);
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
const grouped = new Map<string, import('@allpepper/task-orchestrator/src/domain/types').Task[]>();
|
|
66
|
+
const grouped = new Map<string, Task[]>();
|
|
74
67
|
for (const task of tasksResult.data) {
|
|
75
68
|
if (task.featureId) {
|
|
76
69
|
const list = grouped.get(task.featureId) || [];
|
|
@@ -97,7 +90,7 @@ export function useFeatureKanban(projectId: string): UseFeatureKanbanReturn {
|
|
|
97
90
|
tasks,
|
|
98
91
|
taskCounts: {
|
|
99
92
|
total: tasks.length,
|
|
100
|
-
completed: tasks.filter((t) => t.status
|
|
93
|
+
completed: tasks.filter((t) => isCompletedStatus(t.status)).length,
|
|
101
94
|
},
|
|
102
95
|
};
|
|
103
96
|
});
|
|
@@ -113,7 +106,6 @@ export function useFeatureKanban(projectId: string): UseFeatureKanbanReturn {
|
|
|
113
106
|
|
|
114
107
|
const moveFeature = useCallback(
|
|
115
108
|
async (featureId: string, newStatus: string): Promise<boolean> => {
|
|
116
|
-
// Find feature in current data
|
|
117
109
|
const feature = features.find((f) => f.id === featureId);
|
|
118
110
|
if (!feature) {
|
|
119
111
|
refresh();
|
|
@@ -121,20 +113,43 @@ export function useFeatureKanban(projectId: string): UseFeatureKanbanReturn {
|
|
|
121
113
|
}
|
|
122
114
|
|
|
123
115
|
try {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
feature.version
|
|
128
|
-
);
|
|
116
|
+
// Determine direction based on pipeline position
|
|
117
|
+
const currentIdx = FEATURE_KANBAN_STATUSES.findIndex(s => s.status === feature.status);
|
|
118
|
+
const targetIdx = FEATURE_KANBAN_STATUSES.findIndex(s => s.status === newStatus);
|
|
129
119
|
|
|
130
|
-
if (
|
|
131
|
-
refresh();
|
|
132
|
-
return true;
|
|
133
|
-
} else {
|
|
120
|
+
if (currentIdx === -1 || targetIdx === -1) {
|
|
134
121
|
refresh();
|
|
135
122
|
return false;
|
|
136
123
|
}
|
|
124
|
+
|
|
125
|
+
// Handle terminate (WILL_NOT_IMPLEMENT)
|
|
126
|
+
if (newStatus === 'WILL_NOT_IMPLEMENT') {
|
|
127
|
+
const result = await adapter.terminate('feature', featureId, feature.version);
|
|
128
|
+
refresh();
|
|
129
|
+
return result.success;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Step through the pipeline
|
|
133
|
+
let currentVersion = feature.version;
|
|
134
|
+
const direction = targetIdx > currentIdx ? 'advance' : 'revert';
|
|
135
|
+
const steps = Math.abs(targetIdx - currentIdx);
|
|
136
|
+
|
|
137
|
+
for (let i = 0; i < steps; i++) {
|
|
138
|
+
const result = direction === 'advance'
|
|
139
|
+
? await adapter.advance('feature', featureId, currentVersion)
|
|
140
|
+
: await adapter.revert('feature', featureId, currentVersion);
|
|
141
|
+
|
|
142
|
+
if (!result.success) {
|
|
143
|
+
refresh();
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
currentVersion = result.data.entity.version;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
refresh();
|
|
150
|
+
return true;
|
|
137
151
|
} catch (_err) {
|
|
152
|
+
refresh();
|
|
138
153
|
return false;
|
|
139
154
|
}
|
|
140
155
|
},
|
|
@@ -5,14 +5,14 @@ import type { BoardColumn } from '../lib/types';
|
|
|
5
5
|
import { useBoardData } from './use-data';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* v2 Kanban column definitions (pipeline states)
|
|
9
9
|
*/
|
|
10
10
|
const KANBAN_STATUSES = [
|
|
11
|
-
{ id: '
|
|
12
|
-
{ id: '
|
|
13
|
-
{ id: '
|
|
14
|
-
{ id: '
|
|
15
|
-
{ id: '
|
|
11
|
+
{ id: 'new', title: 'New', status: 'NEW' },
|
|
12
|
+
{ id: 'active', title: 'Active', status: 'ACTIVE' },
|
|
13
|
+
{ id: 'to-be-tested', title: 'To Be Tested', status: 'TO_BE_TESTED' },
|
|
14
|
+
{ id: 'ready-to-prod', title: 'Ready to Prod', status: 'READY_TO_PROD' },
|
|
15
|
+
{ id: 'closed', title: 'Closed', status: 'CLOSED' },
|
|
16
16
|
] as const;
|
|
17
17
|
|
|
18
18
|
interface UseKanbanReturn {
|
|
@@ -24,13 +24,9 @@ interface UseKanbanReturn {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Hook for managing Kanban board state
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* Provides functionality to move tasks between columns with optimistic updates.
|
|
31
|
-
*
|
|
32
|
-
* @param projectId - The project ID to fetch tasks for
|
|
33
|
-
* @returns Kanban board state and operations
|
|
27
|
+
* Hook for managing Kanban board state.
|
|
28
|
+
* In v2, tasks move through the pipeline via advance/revert.
|
|
29
|
+
* The moveTask function uses advance for forward moves and revert for backward moves.
|
|
34
30
|
*/
|
|
35
31
|
export function useKanban(projectId: string): UseKanbanReturn {
|
|
36
32
|
const { adapter } = useAdapter();
|
|
@@ -46,15 +42,10 @@ export function useKanban(projectId: string): UseKanbanReturn {
|
|
|
46
42
|
), [columnsByStatus]);
|
|
47
43
|
|
|
48
44
|
/**
|
|
49
|
-
* Move a task to a new status
|
|
50
|
-
*
|
|
51
|
-
* @param taskId - ID of the task to move
|
|
52
|
-
* @param newStatus - New status to assign
|
|
53
|
-
* @returns true if successful, false otherwise
|
|
45
|
+
* Move a task to a new status via advance/revert pipeline operations
|
|
54
46
|
*/
|
|
55
47
|
const moveTask = useCallback(
|
|
56
48
|
async (taskId: string, newStatus: string): Promise<boolean> => {
|
|
57
|
-
// Find the task in current columns
|
|
58
49
|
let task: Task | undefined;
|
|
59
50
|
for (const column of columns) {
|
|
60
51
|
task = column.tasks.find((t) => t.id === taskId);
|
|
@@ -62,24 +53,41 @@ export function useKanban(projectId: string): UseKanbanReturn {
|
|
|
62
53
|
}
|
|
63
54
|
|
|
64
55
|
if (!task) {
|
|
65
|
-
// Board may be stale; ask the caller to refresh and retry.
|
|
66
56
|
refresh();
|
|
67
57
|
return false;
|
|
68
58
|
}
|
|
69
59
|
|
|
70
60
|
try {
|
|
71
|
-
//
|
|
72
|
-
const
|
|
61
|
+
// Determine direction based on pipeline position
|
|
62
|
+
const currentIdx = KANBAN_STATUSES.findIndex(s => s.status === task!.status);
|
|
63
|
+
const targetIdx = KANBAN_STATUSES.findIndex(s => s.status === newStatus);
|
|
73
64
|
|
|
74
|
-
if (
|
|
75
|
-
// Refresh the board on success
|
|
76
|
-
refresh();
|
|
77
|
-
return true;
|
|
78
|
-
} else {
|
|
65
|
+
if (currentIdx === -1 || targetIdx === -1) {
|
|
79
66
|
refresh();
|
|
80
67
|
return false;
|
|
81
68
|
}
|
|
69
|
+
|
|
70
|
+
// Step through the pipeline one step at a time
|
|
71
|
+
let currentVersion = task.version;
|
|
72
|
+
const direction = targetIdx > currentIdx ? 'advance' : 'revert';
|
|
73
|
+
const steps = Math.abs(targetIdx - currentIdx);
|
|
74
|
+
|
|
75
|
+
for (let i = 0; i < steps; i++) {
|
|
76
|
+
const result = direction === 'advance'
|
|
77
|
+
? await adapter.advance('task', taskId, currentVersion)
|
|
78
|
+
: await adapter.revert('task', taskId, currentVersion);
|
|
79
|
+
|
|
80
|
+
if (!result.success) {
|
|
81
|
+
refresh();
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
currentVersion = result.data.entity.version;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
refresh();
|
|
88
|
+
return true;
|
|
82
89
|
} catch (_err) {
|
|
90
|
+
refresh();
|
|
83
91
|
return false;
|
|
84
92
|
}
|
|
85
93
|
},
|
package/src/ui/index.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
export { darkTheme } from './themes/dark';
|
|
9
9
|
export { lightTheme } from './themes/light';
|
|
10
10
|
export type { Theme, TaskCounts, StatusKey } from './themes/types';
|
|
11
|
+
export type { WorkflowState, TransitionResult } from './adapters/types';
|
|
11
12
|
|
|
12
13
|
// Lib - Types
|
|
13
14
|
export {
|
package/src/ui/lib/colors.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color and Status Utilities
|
|
3
|
+
*
|
|
4
|
+
* Theme-aware color helpers for status badges, priority indicators, etc.
|
|
5
|
+
* Updated for v2 pipeline states.
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
import type { Theme, StatusKey } from '../themes/types';
|
|
2
9
|
import type { Priority } from '@allpepper/task-orchestrator';
|
|
3
10
|
|
|
4
11
|
/**
|
|
5
|
-
* Get the color for a status value
|
|
6
|
-
* Falls back to muted color if status not found
|
|
12
|
+
* Get the color for a status value.
|
|
13
|
+
* Falls back to muted color if status not found.
|
|
7
14
|
*/
|
|
8
15
|
export function getStatusColor(status: string, theme: Theme): string {
|
|
9
16
|
const color = theme.colors.status[status as StatusKey];
|
|
@@ -19,9 +26,6 @@ export function getPriorityColor(priority: Priority, theme: Theme): string {
|
|
|
19
26
|
|
|
20
27
|
/**
|
|
21
28
|
* Get priority dots visual indicator
|
|
22
|
-
* HIGH: ●●● (3 filled)
|
|
23
|
-
* MEDIUM: ●●○ (2 filled)
|
|
24
|
-
* LOW: ●○○ (1 filled)
|
|
25
29
|
*/
|
|
26
30
|
export function getPriorityDots(priority: Priority): string {
|
|
27
31
|
switch (priority) {
|
|
@@ -47,33 +51,32 @@ export function getSemanticColor(
|
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
/**
|
|
50
|
-
*
|
|
54
|
+
* Check if a status represents an active/in-progress state
|
|
51
55
|
*/
|
|
52
56
|
export function isActiveStatus(status: string): boolean {
|
|
53
|
-
|
|
54
|
-
'IN_PROGRESS',
|
|
55
|
-
'IN_DEVELOPMENT',
|
|
56
|
-
'IN_REVIEW',
|
|
57
|
-
'TESTING',
|
|
58
|
-
'VALIDATING',
|
|
59
|
-
'INVESTIGATING',
|
|
60
|
-
'READY_FOR_QA',
|
|
61
|
-
];
|
|
62
|
-
return activeStatuses.includes(status);
|
|
57
|
+
return status === 'ACTIVE' || status === 'TO_BE_TESTED' || status === 'READY_TO_PROD';
|
|
63
58
|
}
|
|
64
59
|
|
|
65
60
|
/**
|
|
66
|
-
*
|
|
61
|
+
* Check if a status represents a terminal/completed state
|
|
67
62
|
*/
|
|
68
|
-
export function
|
|
69
|
-
|
|
70
|
-
return blockedStatuses.includes(status);
|
|
63
|
+
export function isCompletedStatus(status: string): boolean {
|
|
64
|
+
return status === 'CLOSED' || status === 'WILL_NOT_IMPLEMENT';
|
|
71
65
|
}
|
|
72
66
|
|
|
73
67
|
/**
|
|
74
|
-
*
|
|
68
|
+
* Check if a status is the initial state
|
|
75
69
|
*/
|
|
76
|
-
export function
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
export function isNewStatus(status: string): boolean {
|
|
71
|
+
return status === 'NEW';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* In v2, blocking is field-based (blockedBy array), not a status.
|
|
76
|
+
* This function always returns false since no status is "blocked".
|
|
77
|
+
* Use the blockedBy field on the entity instead.
|
|
78
|
+
* @deprecated Use entity.blockedBy.length > 0 instead
|
|
79
|
+
*/
|
|
80
|
+
export function isBlockedStatus(_status: string): boolean {
|
|
81
|
+
return false;
|
|
79
82
|
}
|
package/src/ui/lib/markdown.tsx
CHANGED
|
@@ -64,7 +64,7 @@ function MarkdownLine({ line }: MarkdownLineProps) {
|
|
|
64
64
|
|
|
65
65
|
// Header (## Header)
|
|
66
66
|
const headerMatch = line.match(/^(#{1,6})\s+(.*)$/);
|
|
67
|
-
if (headerMatch) {
|
|
67
|
+
if (headerMatch && headerMatch[1] && headerMatch[2] !== undefined) {
|
|
68
68
|
const level = headerMatch[1].length;
|
|
69
69
|
const text = headerMatch[2];
|
|
70
70
|
return (
|
|
@@ -78,7 +78,7 @@ function MarkdownLine({ line }: MarkdownLineProps) {
|
|
|
78
78
|
|
|
79
79
|
// Bullet point (- item or * item)
|
|
80
80
|
const bulletMatch = line.match(/^(\s*)([-*])\s+(.*)$/);
|
|
81
|
-
if (bulletMatch) {
|
|
81
|
+
if (bulletMatch && bulletMatch[1] !== undefined && bulletMatch[3] !== undefined) {
|
|
82
82
|
const indent = bulletMatch[1].length;
|
|
83
83
|
const content = bulletMatch[3];
|
|
84
84
|
return (
|
|
@@ -114,7 +114,7 @@ function InlineMarkdown({ text }: InlineMarkdownProps) {
|
|
|
114
114
|
while (remaining.length > 0) {
|
|
115
115
|
// Check for inline code `code`
|
|
116
116
|
const codeMatch = remaining.match(/^(.*?)`([^`]+)`(.*)$/);
|
|
117
|
-
if (codeMatch) {
|
|
117
|
+
if (codeMatch && codeMatch[2] !== undefined && codeMatch[3] !== undefined) {
|
|
118
118
|
if (codeMatch[1]) {
|
|
119
119
|
parts.push(<BoldMarkdown key={key++} text={codeMatch[1]} />);
|
|
120
120
|
}
|
|
@@ -129,7 +129,7 @@ function InlineMarkdown({ text }: InlineMarkdownProps) {
|
|
|
129
129
|
|
|
130
130
|
// Check for bold **text**
|
|
131
131
|
const boldMatch = remaining.match(/^(.*?)\*\*([^*]+)\*\*(.*)$/);
|
|
132
|
-
if (boldMatch) {
|
|
132
|
+
if (boldMatch && boldMatch[2] !== undefined && boldMatch[3] !== undefined) {
|
|
133
133
|
if (boldMatch[1]) {
|
|
134
134
|
parts.push(<Text key={key++}>{boldMatch[1]}</Text>);
|
|
135
135
|
}
|
|
@@ -160,7 +160,7 @@ function BoldMarkdown({ text }: { text: string }) {
|
|
|
160
160
|
|
|
161
161
|
while (remaining.length > 0) {
|
|
162
162
|
const boldMatch = remaining.match(/^(.*?)\*\*([^*]+)\*\*(.*)$/);
|
|
163
|
-
if (boldMatch) {
|
|
163
|
+
if (boldMatch && boldMatch[2] !== undefined && boldMatch[3] !== undefined) {
|
|
164
164
|
if (boldMatch[1]) {
|
|
165
165
|
parts.push(<Text key={key++}>{boldMatch[1]}</Text>);
|
|
166
166
|
}
|
package/src/ui/lib/types.ts
CHANGED
package/src/ui/themes/dark.ts
CHANGED
|
@@ -10,37 +10,19 @@ export const darkTheme: Theme = {
|
|
|
10
10
|
muted: '#6b6b8d',
|
|
11
11
|
border: '#3d3d5c',
|
|
12
12
|
|
|
13
|
-
// Status colors (
|
|
13
|
+
// Status colors (v2 pipeline states)
|
|
14
14
|
status: {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ARCHIVED: '#555555',
|
|
22
|
-
|
|
23
|
-
// Feature/Task shared
|
|
24
|
-
TESTING: '#66cccc',
|
|
25
|
-
BLOCKED: '#ff6666',
|
|
26
|
-
DEPLOYED: '#34d399',
|
|
27
|
-
|
|
28
|
-
// Feature only
|
|
29
|
-
DRAFT: '#6b6b8d',
|
|
30
|
-
VALIDATING: '#66cc99',
|
|
31
|
-
PENDING_REVIEW: '#b366ff',
|
|
32
|
-
|
|
33
|
-
// Task only
|
|
34
|
-
BACKLOG: '#6b6b8d',
|
|
35
|
-
PENDING: '#a0a0c0',
|
|
36
|
-
IN_PROGRESS: '#4da6ff',
|
|
37
|
-
IN_REVIEW: '#b366ff',
|
|
38
|
-
CHANGES_REQUESTED: '#ff9966',
|
|
39
|
-
READY_FOR_QA: '#66cc99',
|
|
40
|
-
INVESTIGATING: '#ffcc66',
|
|
41
|
-
DEFERRED: '#999999',
|
|
15
|
+
NEW: '#a0a0c0',
|
|
16
|
+
ACTIVE: '#4da6ff',
|
|
17
|
+
TO_BE_TESTED: '#66cccc',
|
|
18
|
+
READY_TO_PROD: '#66cc99',
|
|
19
|
+
CLOSED: '#4ade80',
|
|
20
|
+
WILL_NOT_IMPLEMENT: '#808080',
|
|
42
21
|
},
|
|
43
22
|
|
|
23
|
+
// Blocked overlay color
|
|
24
|
+
blocked: '#ff6666',
|
|
25
|
+
|
|
44
26
|
// Priority colors
|
|
45
27
|
priority: {
|
|
46
28
|
[Priority.HIGH]: '#ff6b6b',
|
package/src/ui/themes/light.ts
CHANGED
|
@@ -7,57 +7,39 @@ export const lightTheme: Theme = {
|
|
|
7
7
|
// Base colors
|
|
8
8
|
background: '#ffffff',
|
|
9
9
|
foreground: '#1a1a2e',
|
|
10
|
-
muted: '#
|
|
11
|
-
border: '#
|
|
10
|
+
muted: '#9090a0',
|
|
11
|
+
border: '#d0d0e0',
|
|
12
12
|
|
|
13
|
-
// Status colors (
|
|
13
|
+
// Status colors (v2 pipeline states)
|
|
14
14
|
status: {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ARCHIVED: '#9ca3af',
|
|
22
|
-
|
|
23
|
-
// Feature/Task shared
|
|
24
|
-
TESTING: '#06b6d4',
|
|
25
|
-
BLOCKED: '#ef4444',
|
|
26
|
-
DEPLOYED: '#22c55e',
|
|
27
|
-
|
|
28
|
-
// Feature only
|
|
29
|
-
DRAFT: '#9ca3af',
|
|
30
|
-
VALIDATING: '#14b8a6',
|
|
31
|
-
PENDING_REVIEW: '#8b5cf6',
|
|
32
|
-
|
|
33
|
-
// Task only
|
|
34
|
-
BACKLOG: '#9ca3af',
|
|
35
|
-
PENDING: '#6b7280',
|
|
36
|
-
IN_PROGRESS: '#3b82f6',
|
|
37
|
-
IN_REVIEW: '#8b5cf6',
|
|
38
|
-
CHANGES_REQUESTED: '#f97316',
|
|
39
|
-
READY_FOR_QA: '#14b8a6',
|
|
40
|
-
INVESTIGATING: '#eab308',
|
|
41
|
-
DEFERRED: '#9ca3af',
|
|
15
|
+
NEW: '#7070a0',
|
|
16
|
+
ACTIVE: '#2563eb',
|
|
17
|
+
TO_BE_TESTED: '#0891b2',
|
|
18
|
+
READY_TO_PROD: '#059669',
|
|
19
|
+
CLOSED: '#16a34a',
|
|
20
|
+
WILL_NOT_IMPLEMENT: '#6b7280',
|
|
42
21
|
},
|
|
43
22
|
|
|
23
|
+
// Blocked overlay color
|
|
24
|
+
blocked: '#dc2626',
|
|
25
|
+
|
|
44
26
|
// Priority colors
|
|
45
27
|
priority: {
|
|
46
28
|
[Priority.HIGH]: '#dc2626',
|
|
47
|
-
[Priority.MEDIUM]: '#
|
|
29
|
+
[Priority.MEDIUM]: '#d97706',
|
|
48
30
|
[Priority.LOW]: '#16a34a',
|
|
49
31
|
},
|
|
50
32
|
|
|
51
33
|
// Semantic colors
|
|
52
34
|
accent: '#2563eb',
|
|
53
35
|
success: '#16a34a',
|
|
54
|
-
warning: '#
|
|
36
|
+
warning: '#d97706',
|
|
55
37
|
error: '#dc2626',
|
|
56
38
|
danger: '#dc2626',
|
|
57
39
|
info: '#0891b2',
|
|
58
40
|
|
|
59
41
|
// Interactive colors
|
|
60
|
-
selection: '#
|
|
61
|
-
highlight: '#
|
|
42
|
+
selection: '#e0e0f0',
|
|
43
|
+
highlight: '#c0c0e0',
|
|
62
44
|
},
|
|
63
45
|
};
|
package/src/ui/themes/types.ts
CHANGED
|
@@ -1,33 +1,18 @@
|
|
|
1
1
|
import type { Priority } from '@allpepper/task-orchestrator';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* v2 pipeline status keys
|
|
5
|
+
* Task: NEW, ACTIVE, TO_BE_TESTED, READY_TO_PROD, CLOSED, WILL_NOT_IMPLEMENT
|
|
6
|
+
* Feature: NEW, ACTIVE, READY_TO_PROD, CLOSED, WILL_NOT_IMPLEMENT
|
|
7
|
+
* Projects: stateless (no status)
|
|
6
8
|
*/
|
|
7
9
|
export type StatusKey =
|
|
8
|
-
|
|
9
|
-
| '
|
|
10
|
-
| '
|
|
11
|
-
| '
|
|
12
|
-
| '
|
|
13
|
-
| '
|
|
14
|
-
| 'ARCHIVED'
|
|
15
|
-
// Feature-only statuses
|
|
16
|
-
| 'DRAFT'
|
|
17
|
-
| 'TESTING'
|
|
18
|
-
| 'VALIDATING'
|
|
19
|
-
| 'PENDING_REVIEW'
|
|
20
|
-
| 'BLOCKED'
|
|
21
|
-
| 'DEPLOYED'
|
|
22
|
-
// Task-only statuses
|
|
23
|
-
| 'BACKLOG'
|
|
24
|
-
| 'PENDING'
|
|
25
|
-
| 'IN_PROGRESS'
|
|
26
|
-
| 'IN_REVIEW'
|
|
27
|
-
| 'CHANGES_REQUESTED'
|
|
28
|
-
| 'READY_FOR_QA'
|
|
29
|
-
| 'INVESTIGATING'
|
|
30
|
-
| 'DEFERRED';
|
|
10
|
+
| 'NEW'
|
|
11
|
+
| 'ACTIVE'
|
|
12
|
+
| 'TO_BE_TESTED'
|
|
13
|
+
| 'READY_TO_PROD'
|
|
14
|
+
| 'CLOSED'
|
|
15
|
+
| 'WILL_NOT_IMPLEMENT';
|
|
31
16
|
|
|
32
17
|
/**
|
|
33
18
|
* Theme interface for the Task Orchestrator UI
|
|
@@ -42,9 +27,12 @@ export interface Theme {
|
|
|
42
27
|
muted: string;
|
|
43
28
|
border: string;
|
|
44
29
|
|
|
45
|
-
// Status colors - maps
|
|
30
|
+
// Status colors - maps v2 pipeline states
|
|
46
31
|
status: Record<StatusKey, string>;
|
|
47
32
|
|
|
33
|
+
// Blocked overlay color (for field-based blocking indicator)
|
|
34
|
+
blocked: string;
|
|
35
|
+
|
|
48
36
|
// Priority colors
|
|
49
37
|
priority: Record<Priority, string>;
|
|
50
38
|
|