@critical-path/core 0.13.3 → 0.15.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/CHANGELOG.md +24 -0
- package/README.md +22 -0
- package/dist/deliverables.test.js +2 -2
- package/dist/deliverables.test.js.map +1 -1
- package/dist/domain/entities.d.ts +2 -1
- package/dist/domain/entities.d.ts.map +1 -1
- package/dist/domain/entities.js +11 -4
- package/dist/domain/entities.js.map +1 -1
- package/dist/engine/index.d.ts +8 -3
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +56 -13
- package/dist/engine/index.js.map +1 -1
- package/dist/index.test.js +8 -8
- package/dist/index.test.js.map +1 -1
- package/dist/status.test.d.ts +2 -0
- package/dist/status.test.d.ts.map +1 -0
- package/dist/status.test.js +199 -0
- package/dist/status.test.js.map +1 -0
- package/dist/storage/file-storage.d.ts +1 -0
- package/dist/storage/file-storage.d.ts.map +1 -1
- package/dist/storage/file-storage.js +7 -0
- package/dist/storage/file-storage.js.map +1 -1
- package/dist/storage/firebase-storage.d.ts +2 -0
- package/dist/storage/firebase-storage.d.ts.map +1 -1
- package/dist/storage/firebase-storage.js +20 -0
- package/dist/storage/firebase-storage.js.map +1 -1
- package/dist/storage/storage.test.js +32 -0
- package/dist/storage/storage.test.js.map +1 -1
- package/dist/store/firebase.test.js +1 -1
- package/dist/store/firebase.test.js.map +1 -1
- package/dist/store/sqlite.test.js +1 -1
- package/dist/store/sqlite.test.js.map +1 -1
- package/dist/types/index.d.ts +22 -5
- package/dist/types/index.d.ts.map +1 -1
- package/dist/use-case-validation.test.js +1 -1
- package/dist/use-case-validation.test.js.map +1 -1
- package/dist/utils/status.d.ts +7 -10
- package/dist/utils/status.d.ts.map +1 -1
- package/dist/utils/status.js +104 -21
- package/dist/utils/status.js.map +1 -1
- package/dist/utils/workflow.js +15 -15
- package/dist/utils/workflow.js.map +1 -1
- package/package.json +1 -1
- package/src/deliverables.test.ts +2 -2
- package/src/domain/entities.ts +15 -5
- package/src/engine/index.ts +71 -16
- package/src/index.test.ts +8 -8
- package/src/status.test.ts +226 -0
- package/src/storage/file-storage.ts +8 -0
- package/src/storage/firebase-storage.ts +20 -0
- package/src/storage/storage.test.ts +40 -0
- package/src/store/firebase.test.ts +1 -1
- package/src/store/sqlite.test.ts +1 -1
- package/src/types/index.ts +24 -5
- package/src/use-case-validation.test.ts +1 -1
- package/src/utils/status.ts +119 -33
- package/src/utils/workflow.ts +15 -15
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -52,6 +52,11 @@ export class InMemoryFirebaseStorageMock implements FirebaseStorageBucketInterfa
|
|
|
52
52
|
async delete() {
|
|
53
53
|
files.delete(path);
|
|
54
54
|
},
|
|
55
|
+
async download(): Promise<[Uint8Array]> {
|
|
56
|
+
const file = files.get(path);
|
|
57
|
+
if (!file) throw new Error(`File not found: ${path}`);
|
|
58
|
+
return [file.data];
|
|
59
|
+
},
|
|
55
60
|
async getMetadata(): Promise<[any]> {
|
|
56
61
|
const file = files.get(path);
|
|
57
62
|
return [{
|
|
@@ -146,6 +151,21 @@ export class FirebaseStorageAdapter implements FileStorageAdapter {
|
|
|
146
151
|
}
|
|
147
152
|
}
|
|
148
153
|
|
|
154
|
+
async download(storageKey: string): Promise<Buffer | Uint8Array> {
|
|
155
|
+
const fileRef = this.bucket.file(storageKey);
|
|
156
|
+
if (typeof (fileRef as any).download === 'function') {
|
|
157
|
+
const [buf] = await (fileRef as any).download();
|
|
158
|
+
return buf;
|
|
159
|
+
}
|
|
160
|
+
const url = await this.getDownloadUrl(storageKey);
|
|
161
|
+
const res = await fetch(url);
|
|
162
|
+
if (!res.ok) {
|
|
163
|
+
throw new Error(`Failed to download file from "${url}": ${res.statusText}`);
|
|
164
|
+
}
|
|
165
|
+
const arrayBuffer = await res.arrayBuffer();
|
|
166
|
+
return new Uint8Array(arrayBuffer);
|
|
167
|
+
}
|
|
168
|
+
|
|
149
169
|
async getDownloadUrl(storageKey: string): Promise<string> {
|
|
150
170
|
const fileRef = this.bucket.file(storageKey);
|
|
151
171
|
if (typeof (fileRef as any).getMetadata === 'function') {
|
|
@@ -172,5 +172,45 @@ describe('Storage Adapters', () => {
|
|
|
172
172
|
expect(storedUri?.sizeBytes).toBe(Buffer.from(rawText).length);
|
|
173
173
|
expect(new TextDecoder().decode(storedUri?.data)).toBe(rawText);
|
|
174
174
|
});
|
|
175
|
+
|
|
176
|
+
it('downloads file bytes and reads text content from attachments via engine', async () => {
|
|
177
|
+
const mockStorage = new InMemoryFirebaseStorageMock();
|
|
178
|
+
const storageAdapter = new FirebaseStorageAdapter({ bucket: mockStorage, bucketName: 'test-bucket' });
|
|
179
|
+
const engine = new CriticalPathEngine({ fileStorage: storageAdapter });
|
|
180
|
+
|
|
181
|
+
const project = await engine.createProject({ name: 'Attachment Reading Test' });
|
|
182
|
+
const task = await engine.createTask({ projectId: project.id, title: 'Document Task' });
|
|
183
|
+
|
|
184
|
+
const planMarkdown = '# Project Roadmap\n\nPhase 1: Research\nPhase 2: Execution';
|
|
185
|
+
const attachment = await engine.uploadAttachmentFile({
|
|
186
|
+
filename: 'Roadmap.md',
|
|
187
|
+
data: planMarkdown,
|
|
188
|
+
mimeType: 'text/markdown',
|
|
189
|
+
taskId: task.id,
|
|
190
|
+
projectId: project.id,
|
|
191
|
+
uploaderId: 'agent_planner',
|
|
192
|
+
uploaderType: 'agent',
|
|
193
|
+
artifactType: 'plan'
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
expect(attachment.id).toBeDefined();
|
|
197
|
+
expect(attachment.artifactType).toBe('plan');
|
|
198
|
+
|
|
199
|
+
// Test download method on adapter
|
|
200
|
+
const downloadedBytes = await storageAdapter.download(attachment.storageKey!);
|
|
201
|
+
expect(new TextDecoder().decode(downloadedBytes)).toBe(planMarkdown);
|
|
202
|
+
|
|
203
|
+
// Test engine readAttachmentText
|
|
204
|
+
const readText = await engine.readAttachmentText(attachment.id);
|
|
205
|
+
expect(readText).toBe(planMarkdown);
|
|
206
|
+
|
|
207
|
+
// Test filtering by artifactType
|
|
208
|
+
const planAttachments = await engine.getAttachments({ taskId: task.id, artifactType: 'plan' });
|
|
209
|
+
expect(planAttachments.length).toBe(1);
|
|
210
|
+
expect(planAttachments[0].id).toBe(attachment.id);
|
|
211
|
+
|
|
212
|
+
const reviewAttachments = await engine.getAttachments({ taskId: task.id, artifactType: 'review' });
|
|
213
|
+
expect(reviewAttachments.length).toBe(0);
|
|
214
|
+
});
|
|
175
215
|
});
|
|
176
216
|
});
|
|
@@ -62,7 +62,7 @@ describe('FirebaseStore', () => {
|
|
|
62
62
|
name: 'Firebase Workflow',
|
|
63
63
|
isDefault: false,
|
|
64
64
|
defaultStatusKey: 'backlog',
|
|
65
|
-
statuses: [{ key: 'backlog', label: 'Backlog',
|
|
65
|
+
statuses: [{ key: 'backlog', label: 'Backlog', category: 'not_started' }],
|
|
66
66
|
transitions: []
|
|
67
67
|
});
|
|
68
68
|
|
package/src/store/sqlite.test.ts
CHANGED
|
@@ -155,7 +155,7 @@ describe('SQLiteStore', () => {
|
|
|
155
155
|
name: 'SQLite Workflow',
|
|
156
156
|
isDefault: true,
|
|
157
157
|
defaultStatusKey: 'todo',
|
|
158
|
-
statuses: [{ key: 'todo', label: 'To Do',
|
|
158
|
+
statuses: [{ key: 'todo', label: 'To Do', category: 'not_started' }],
|
|
159
159
|
transitions: []
|
|
160
160
|
});
|
|
161
161
|
|
package/src/types/index.ts
CHANGED
|
@@ -4,17 +4,33 @@ export type Priority = DefaultPriority | (string & {});
|
|
|
4
4
|
export type DefaultTaskStatus = 'backlog' | 'todo' | 'in_progress' | 'in_review' | 'done' | 'canceled';
|
|
5
5
|
export type TaskStatus = DefaultTaskStatus | (string & {});
|
|
6
6
|
|
|
7
|
-
export type
|
|
8
|
-
export type ExecutionState = 'active' | 'inactive';
|
|
7
|
+
export type SemanticStatus = 'not_started' | 'in_progress' | 'completed' | 'canceled';
|
|
9
8
|
|
|
10
9
|
export interface StatusDefinition {
|
|
11
10
|
key: string;
|
|
12
11
|
label: string;
|
|
13
|
-
|
|
14
|
-
executionState: ExecutionState;
|
|
15
|
-
isCancelled?: boolean;
|
|
12
|
+
category: SemanticStatus;
|
|
16
13
|
}
|
|
17
14
|
|
|
15
|
+
export interface TaskDerivedStatus {
|
|
16
|
+
semanticStatus: SemanticStatus;
|
|
17
|
+
isReady: boolean;
|
|
18
|
+
isBlocked: boolean;
|
|
19
|
+
blockingTaskIds: string[];
|
|
20
|
+
isOverdue: boolean;
|
|
21
|
+
isUpcoming: boolean;
|
|
22
|
+
isUnplanned: boolean;
|
|
23
|
+
isUnassigned: boolean;
|
|
24
|
+
isStalled: boolean;
|
|
25
|
+
isOverEstimate: boolean;
|
|
26
|
+
isPaceWarning: boolean;
|
|
27
|
+
isDone: boolean;
|
|
28
|
+
isActive: boolean;
|
|
29
|
+
isCancelled: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type TaskLifecycleState = TaskDerivedStatus;
|
|
33
|
+
|
|
18
34
|
export type Role = 'admin' | 'project_manager' | 'contributor' | 'viewer';
|
|
19
35
|
|
|
20
36
|
export interface User {
|
|
@@ -142,6 +158,7 @@ export interface Task {
|
|
|
142
158
|
title: string;
|
|
143
159
|
description?: string;
|
|
144
160
|
status: TaskStatus;
|
|
161
|
+
semanticStatus?: SemanticStatus;
|
|
145
162
|
priority: Priority;
|
|
146
163
|
taskType?: string; // e.g. "bug", "feature", "task", "epic"
|
|
147
164
|
assigneeId?: string;
|
|
@@ -271,6 +288,7 @@ export interface Attachment {
|
|
|
271
288
|
sizeBytes: number;
|
|
272
289
|
url: string;
|
|
273
290
|
storageKey?: string;
|
|
291
|
+
artifactType?: 'plan' | 'spec' | 'deliverable' | 'review' | 'general';
|
|
274
292
|
metadata?: Record<string, unknown>;
|
|
275
293
|
createdAt: string;
|
|
276
294
|
updatedAt: string;
|
|
@@ -309,6 +327,7 @@ export interface PresignedUploadResult {
|
|
|
309
327
|
export interface FileStorageAdapter {
|
|
310
328
|
upload(input: UploadFileInput): Promise<UploadFileResult>;
|
|
311
329
|
delete(storageKey: string): Promise<boolean>;
|
|
330
|
+
download?(storageKey: string): Promise<Buffer | Uint8Array>;
|
|
312
331
|
getDownloadUrl?(storageKey: string): Promise<string>;
|
|
313
332
|
getPresignedUploadUrl?(options: PresignedUrlOptions): Promise<PresignedUploadResult>;
|
|
314
333
|
}
|
|
@@ -138,7 +138,7 @@ describe('Critical Path Use Case Validation Suite', () => {
|
|
|
138
138
|
// Check Lifecycle State
|
|
139
139
|
const lifecycle = await engine.getTaskLifecycleState(featureTask.id);
|
|
140
140
|
expect(lifecycle?.isDone).toBe(true);
|
|
141
|
-
expect(lifecycle?.
|
|
141
|
+
expect(lifecycle?.semanticStatus).toBe('completed');
|
|
142
142
|
});
|
|
143
143
|
});
|
|
144
144
|
|
package/src/utils/status.ts
CHANGED
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
Task,
|
|
3
3
|
StatusDefinition,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
SemanticStatus,
|
|
5
|
+
TaskDerivedStatus,
|
|
6
6
|
} from '../types/index.js';
|
|
7
7
|
|
|
8
8
|
export const DEFAULT_STATUS_DEFINITIONS: Record<string, StatusDefinition> = {
|
|
9
|
-
backlog: { key: 'backlog', label: 'Backlog',
|
|
10
|
-
todo: { key: 'todo', label: 'To Do',
|
|
11
|
-
in_progress: { key: 'in_progress', label: 'In Progress',
|
|
12
|
-
in_review: { key: 'in_review', label: 'In Review',
|
|
13
|
-
done: { key: 'done', label: 'Done',
|
|
14
|
-
canceled: { key: 'canceled', label: 'Canceled',
|
|
9
|
+
backlog: { key: 'backlog', label: 'Backlog', category: 'not_started' },
|
|
10
|
+
todo: { key: 'todo', label: 'To Do', category: 'not_started' },
|
|
11
|
+
in_progress: { key: 'in_progress', label: 'In Progress', category: 'in_progress' },
|
|
12
|
+
in_review: { key: 'in_review', label: 'In Review', category: 'in_progress' },
|
|
13
|
+
done: { key: 'done', label: 'Done', category: 'completed' },
|
|
14
|
+
canceled: { key: 'canceled', label: 'Canceled', category: 'canceled' }
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
export interface
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
isCancelled: boolean;
|
|
23
|
-
isOverdue: boolean;
|
|
24
|
-
isUpcoming: boolean;
|
|
17
|
+
export interface DeriveTaskLifecycleOptions {
|
|
18
|
+
customDefinitions?: StatusDefinition[];
|
|
19
|
+
referenceDate?: Date;
|
|
20
|
+
upstreamTasks?: Task[];
|
|
21
|
+
stalledThresholdDays?: number;
|
|
25
22
|
}
|
|
26
23
|
|
|
27
24
|
export function resolveStatusDefinition(
|
|
@@ -35,28 +32,66 @@ export function resolveStatusDefinition(
|
|
|
35
32
|
if (DEFAULT_STATUS_DEFINITIONS[statusKey]) {
|
|
36
33
|
return DEFAULT_STATUS_DEFINITIONS[statusKey];
|
|
37
34
|
}
|
|
35
|
+
|
|
38
36
|
// Fallback heuristic for unknown custom statuses:
|
|
39
|
-
const
|
|
40
|
-
|
|
37
|
+
const lower = statusKey.toLowerCase();
|
|
38
|
+
let category: SemanticStatus = 'not_started';
|
|
39
|
+
if (['canceled', 'cancelled', 'rejected', 'abandoned', 'void', 'dropped'].some((k) => lower.includes(k))) {
|
|
40
|
+
category = 'canceled';
|
|
41
|
+
} else if (['done', 'completed', 'finished', 'closed', 'approved', 'final', 'delivered', 'shipped'].some((k) => lower.includes(k))) {
|
|
42
|
+
category = 'completed';
|
|
43
|
+
} else if (['in_progress', 'active', 'working', 'in_review', 'review', 'testing', 'qa', 'building', 'editing'].some((k) => lower.includes(k))) {
|
|
44
|
+
category = 'in_progress';
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
return {
|
|
42
48
|
key: statusKey,
|
|
43
49
|
label: statusKey,
|
|
44
|
-
|
|
45
|
-
executionState: isActiveKey ? 'active' : 'inactive',
|
|
46
|
-
isCancelled: statusKey.toLowerCase().includes('cancel')
|
|
50
|
+
category
|
|
47
51
|
};
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
export function deriveTaskLifecycleState(
|
|
51
55
|
task: Task,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
):
|
|
56
|
+
customDefinitionsOrOptions?: StatusDefinition[] | DeriveTaskLifecycleOptions,
|
|
57
|
+
legacyReferenceDate?: Date
|
|
58
|
+
): TaskDerivedStatus {
|
|
59
|
+
let customDefinitions: StatusDefinition[] | undefined;
|
|
60
|
+
let referenceDate = legacyReferenceDate ?? new Date();
|
|
61
|
+
let upstreamTasks: Task[] = [];
|
|
62
|
+
let stalledThresholdDays = 5;
|
|
63
|
+
|
|
64
|
+
if (Array.isArray(customDefinitionsOrOptions)) {
|
|
65
|
+
customDefinitions = customDefinitionsOrOptions;
|
|
66
|
+
} else if (customDefinitionsOrOptions) {
|
|
67
|
+
customDefinitions = customDefinitionsOrOptions.customDefinitions;
|
|
68
|
+
if (customDefinitionsOrOptions.referenceDate) {
|
|
69
|
+
referenceDate = customDefinitionsOrOptions.referenceDate;
|
|
70
|
+
}
|
|
71
|
+
if (customDefinitionsOrOptions.upstreamTasks) {
|
|
72
|
+
upstreamTasks = customDefinitionsOrOptions.upstreamTasks;
|
|
73
|
+
}
|
|
74
|
+
if (customDefinitionsOrOptions.stalledThresholdDays !== undefined) {
|
|
75
|
+
stalledThresholdDays = customDefinitionsOrOptions.stalledThresholdDays;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
55
79
|
const def = resolveStatusDefinition(task.status, customDefinitions);
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
80
|
+
const semanticStatus: SemanticStatus = def.category;
|
|
81
|
+
const isDone = semanticStatus === 'completed' || semanticStatus === 'canceled';
|
|
82
|
+
const isActive = semanticStatus === 'in_progress';
|
|
83
|
+
const isCancelled = semanticStatus === 'canceled';
|
|
59
84
|
|
|
85
|
+
// Dependency & Flow Indicators
|
|
86
|
+
const blockingTasks = upstreamTasks.filter((t) => {
|
|
87
|
+
const upstreamDef = resolveStatusDefinition(t.status, customDefinitions);
|
|
88
|
+
return upstreamDef.category !== 'completed';
|
|
89
|
+
});
|
|
90
|
+
const blockingTaskIds = blockingTasks.map((t) => t.id);
|
|
91
|
+
const isBlocked = !isDone && blockingTaskIds.length > 0;
|
|
92
|
+
const isReady = semanticStatus === 'not_started' && blockingTaskIds.length === 0;
|
|
93
|
+
|
|
94
|
+
// Time & Schedule Indicators
|
|
60
95
|
let isOverdue = false;
|
|
61
96
|
if (!isDone && task.dueDate) {
|
|
62
97
|
const due = new Date(task.dueDate);
|
|
@@ -66,20 +101,71 @@ export function deriveTaskLifecycleState(
|
|
|
66
101
|
}
|
|
67
102
|
|
|
68
103
|
let isUpcoming = false;
|
|
69
|
-
if (
|
|
104
|
+
if (semanticStatus === 'not_started' && task.plannedStartDate) {
|
|
70
105
|
const plannedStart = new Date(task.plannedStartDate);
|
|
71
106
|
if (!isNaN(plannedStart.getTime()) && plannedStart > referenceDate) {
|
|
72
107
|
isUpcoming = true;
|
|
73
108
|
}
|
|
74
109
|
}
|
|
75
110
|
|
|
111
|
+
const isUnplanned = semanticStatus === 'not_started' && !task.iterationId && !task.plannedStartDate && !task.dueDate;
|
|
112
|
+
|
|
113
|
+
// Resource & Activity Indicators
|
|
114
|
+
const hasAssignee = Boolean(task.assigneeId || (task.assignees && task.assignees.length > 0));
|
|
115
|
+
const isUnassigned = !isDone && !hasAssignee;
|
|
116
|
+
|
|
117
|
+
let isStalled = false;
|
|
118
|
+
if (semanticStatus === 'in_progress') {
|
|
119
|
+
const lastActivity = task.updatedAt ? new Date(task.updatedAt) : (task.createdAt ? new Date(task.createdAt) : null);
|
|
120
|
+
if (lastActivity && !isNaN(lastActivity.getTime())) {
|
|
121
|
+
const thresholdMs = stalledThresholdDays * 24 * 60 * 60 * 1000;
|
|
122
|
+
if (referenceDate.getTime() - lastActivity.getTime() > thresholdMs) {
|
|
123
|
+
isStalled = true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Effort & Estimate Indicators
|
|
129
|
+
let isOverEstimate = false;
|
|
130
|
+
const loggedH = task.loggedHours ?? task.actualHours;
|
|
131
|
+
const estimatedH = task.estimatedHours;
|
|
132
|
+
if (estimatedH !== undefined && estimatedH > 0 && loggedH !== undefined && loggedH > estimatedH) {
|
|
133
|
+
isOverEstimate = true;
|
|
134
|
+
}
|
|
135
|
+
const actualM = task.actualDurationMinutes;
|
|
136
|
+
const estimatedM = task.estimatedDurationMinutes;
|
|
137
|
+
if (estimatedM !== undefined && estimatedM > 0 && actualM !== undefined && actualM > estimatedM) {
|
|
138
|
+
isOverEstimate = true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let isPaceWarning = false;
|
|
142
|
+
if (semanticStatus === 'in_progress' && task.actualStartDate) {
|
|
143
|
+
const start = new Date(task.actualStartDate);
|
|
144
|
+
if (!isNaN(start.getTime())) {
|
|
145
|
+
const elapsedMs = referenceDate.getTime() - start.getTime();
|
|
146
|
+
const elapsedHours = elapsedMs / (1000 * 60 * 60);
|
|
147
|
+
if (estimatedH !== undefined && estimatedH > 0 && elapsedHours > estimatedH) {
|
|
148
|
+
isPaceWarning = true;
|
|
149
|
+
} else if (estimatedM !== undefined && estimatedM > 0 && (elapsedMs / (1000 * 60)) > estimatedM) {
|
|
150
|
+
isPaceWarning = true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
76
155
|
return {
|
|
77
|
-
|
|
78
|
-
|
|
156
|
+
semanticStatus,
|
|
157
|
+
isReady,
|
|
158
|
+
isBlocked,
|
|
159
|
+
blockingTaskIds,
|
|
160
|
+
isOverdue,
|
|
161
|
+
isUpcoming,
|
|
162
|
+
isUnplanned,
|
|
163
|
+
isUnassigned,
|
|
164
|
+
isStalled,
|
|
165
|
+
isOverEstimate,
|
|
166
|
+
isPaceWarning,
|
|
79
167
|
isDone,
|
|
80
168
|
isActive,
|
|
81
|
-
isCancelled
|
|
82
|
-
isOverdue,
|
|
83
|
-
isUpcoming
|
|
169
|
+
isCancelled
|
|
84
170
|
};
|
|
85
171
|
}
|
package/src/utils/workflow.ts
CHANGED
|
@@ -90,13 +90,13 @@ export const DEFAULT_VFX_WORKFLOW: Workflow = {
|
|
|
90
90
|
name: 'VFX Production Workflow',
|
|
91
91
|
description: 'Visual effects production pipeline from bidding and drafting to internal lead review and client supervisor approval.',
|
|
92
92
|
statuses: [
|
|
93
|
-
{ key: 'bidding', label: 'Bidding & Draft',
|
|
94
|
-
{ key: 'awarded', label: 'Awarded / Ready',
|
|
95
|
-
{ key: 'in_production', label: 'In Production',
|
|
96
|
-
{ key: 'internal_review', label: 'Internal Review',
|
|
97
|
-
{ key: 'client_review', label: 'Client Review',
|
|
98
|
-
{ key: 'revision_requested', label: 'Revision Requested',
|
|
99
|
-
{ key: 'approved', label: 'Approved (Final)',
|
|
93
|
+
{ key: 'bidding', label: 'Bidding & Draft', category: 'not_started' },
|
|
94
|
+
{ key: 'awarded', label: 'Awarded / Ready', category: 'not_started' },
|
|
95
|
+
{ key: 'in_production', label: 'In Production', category: 'in_progress' },
|
|
96
|
+
{ key: 'internal_review', label: 'Internal Review', category: 'in_progress' },
|
|
97
|
+
{ key: 'client_review', label: 'Client Review', category: 'in_progress' },
|
|
98
|
+
{ key: 'revision_requested', label: 'Revision Requested', category: 'in_progress' },
|
|
99
|
+
{ key: 'approved', label: 'Approved (Final)', category: 'completed' },
|
|
100
100
|
DEFAULT_STATUS_DEFINITIONS.canceled
|
|
101
101
|
],
|
|
102
102
|
transitions: [
|
|
@@ -126,14 +126,14 @@ export const DEFAULT_CREATIVE_WORKFLOW: Workflow = {
|
|
|
126
126
|
name: 'Creative Production Workflow',
|
|
127
127
|
description: 'Production pipeline for creative deliverables from briefing and concepts to internal review, client feedback, and final delivery.',
|
|
128
128
|
statuses: [
|
|
129
|
-
{ key: 'briefing', label: 'Briefing & Scoping',
|
|
130
|
-
{ key: 'concept', label: 'Concept & Storyboarding',
|
|
131
|
-
{ key: 'in_production', label: 'In Production',
|
|
132
|
-
{ key: 'internal_review', label: 'Internal Review',
|
|
133
|
-
{ key: 'client_review', label: 'Client Review',
|
|
134
|
-
{ key: 'revision_requested', label: 'Revision Requested',
|
|
135
|
-
{ key: 'approved', label: 'Approved',
|
|
136
|
-
{ key: 'delivered', label: 'Delivered',
|
|
129
|
+
{ key: 'briefing', label: 'Briefing & Scoping', category: 'not_started' },
|
|
130
|
+
{ key: 'concept', label: 'Concept & Storyboarding', category: 'in_progress' },
|
|
131
|
+
{ key: 'in_production', label: 'In Production', category: 'in_progress' },
|
|
132
|
+
{ key: 'internal_review', label: 'Internal Review', category: 'in_progress' },
|
|
133
|
+
{ key: 'client_review', label: 'Client Review', category: 'in_progress' },
|
|
134
|
+
{ key: 'revision_requested', label: 'Revision Requested', category: 'in_progress' },
|
|
135
|
+
{ key: 'approved', label: 'Approved', category: 'completed' },
|
|
136
|
+
{ key: 'delivered', label: 'Delivered', category: 'completed' },
|
|
137
137
|
DEFAULT_STATUS_DEFINITIONS.canceled
|
|
138
138
|
],
|
|
139
139
|
transitions: [
|
package/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/deliverables.test.ts","./src/index.test.ts","./src/index.ts","./src/use-case-validation.test.ts","./src/domain/custom-fields.ts","./src/domain/ddd.test.ts","./src/domain/entities.ts","./src/domain/events.ts","./src/domain/graph.ts","./src/domain/index.ts","./src/engine/index.ts","./src/plugins/index.ts","./src/storage/file-storage.ts","./src/storage/firebase-storage.ts","./src/storage/index.ts","./src/storage/s3.ts","./src/storage/storage.test.ts","./src/store/firebase.test.ts","./src/store/firebase.ts","./src/store/index.ts","./src/store/sqlite.test.ts","./src/store/sqlite.ts","./src/types/index.ts","./src/utils/index.ts","./src/utils/key.test.ts","./src/utils/key.ts","./src/utils/mentions.test.ts","./src/utils/mentions.ts","./src/utils/status.ts","./src/utils/workflow.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["./src/deliverables.test.ts","./src/index.test.ts","./src/index.ts","./src/status.test.ts","./src/use-case-validation.test.ts","./src/domain/custom-fields.ts","./src/domain/ddd.test.ts","./src/domain/entities.ts","./src/domain/events.ts","./src/domain/graph.ts","./src/domain/index.ts","./src/engine/index.ts","./src/plugins/index.ts","./src/storage/file-storage.ts","./src/storage/firebase-storage.ts","./src/storage/index.ts","./src/storage/s3.ts","./src/storage/storage.test.ts","./src/store/firebase.test.ts","./src/store/firebase.ts","./src/store/index.ts","./src/store/sqlite.test.ts","./src/store/sqlite.ts","./src/types/index.ts","./src/utils/index.ts","./src/utils/key.test.ts","./src/utils/key.ts","./src/utils/mentions.test.ts","./src/utils/mentions.ts","./src/utils/status.ts","./src/utils/workflow.ts"],"version":"5.9.3"}
|