@tuturuuu/utils 0.0.3 → 0.6.1
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 +313 -0
- package/biome.json +5 -0
- package/jsr.json +8 -8
- package/package.json +63 -32
- package/src/__tests__/ai-temp-auth.test.ts +309 -0
- package/src/__tests__/api-proxy-guard.test.ts +1451 -0
- package/src/__tests__/app-url.test.ts +270 -0
- package/src/__tests__/avatar-url.test.ts +97 -0
- package/src/__tests__/color-helper.test.ts +179 -0
- package/src/__tests__/constants.test.ts +351 -0
- package/src/__tests__/crypto.test.ts +107 -0
- package/src/__tests__/date-helper.test.ts +408 -0
- package/src/__tests__/fixtures/task-description-full-featured.json +456 -0
- package/src/__tests__/format.test.ts +317 -0
- package/src/__tests__/html-sanitizer.test.ts +360 -0
- package/src/__tests__/interest-calculator.test.ts +336 -0
- package/src/__tests__/interest-detector.test.ts +222 -0
- package/src/__tests__/label-colors.test.ts +241 -0
- package/src/__tests__/name-helper.test.ts +158 -0
- package/src/__tests__/node-diff.test.ts +576 -0
- package/src/__tests__/notification-service.test.ts +210 -0
- package/src/__tests__/onboarding-helper.test.ts +331 -0
- package/src/__tests__/path-helper.test.ts +152 -0
- package/src/__tests__/permissions.test.tsx +81 -0
- package/src/__tests__/request-emoji-limit.test.ts +172 -0
- package/src/__tests__/search-helper.test.ts +51 -0
- package/src/__tests__/storage-display-name.test.ts +37 -0
- package/src/__tests__/storage-path.test.ts +238 -0
- package/src/__tests__/tag-utils.test.ts +205 -0
- package/src/__tests__/task-description-yjs-state.test.ts +581 -0
- package/src/__tests__/task-helper-board-api-routing.test.ts +94 -0
- package/src/__tests__/task-helper-create-task.test.ts +129 -0
- package/src/__tests__/task-helpers.test.ts +464 -0
- package/src/__tests__/task-overrides.test.ts +305 -0
- package/src/__tests__/task-reorder-cache.test.ts +74 -0
- package/src/__tests__/task-sort-keys.test.ts +36 -0
- package/src/__tests__/task-transformers.test.ts +62 -0
- package/src/__tests__/text-helper.test.ts +776 -0
- package/src/__tests__/time-helper.test.ts +70 -0
- package/src/__tests__/time-tracker-period.test.ts +55 -0
- package/src/__tests__/timezone.test.ts +117 -0
- package/src/__tests__/upstash-rest.test.ts +77 -0
- package/src/__tests__/uuid-helper.test.ts +133 -0
- package/src/__tests__/workspace-helper.test.ts +859 -0
- package/src/__tests__/workspace-limits.test.ts +255 -0
- package/src/__tests__/yjs-helper.test.ts +581 -0
- package/src/abuse-protection/__tests__/backend-rate-limit.test.ts +113 -0
- package/src/abuse-protection/__tests__/edge.test.ts +136 -0
- package/src/abuse-protection/__tests__/index.test.ts +562 -0
- package/src/abuse-protection/__tests__/reputation.test.ts +192 -0
- package/src/abuse-protection/backend-rate-limit.ts +44 -0
- package/src/abuse-protection/constants.ts +117 -0
- package/src/abuse-protection/edge.ts +223 -0
- package/src/abuse-protection/index.ts +1545 -0
- package/src/abuse-protection/reputation.ts +587 -0
- package/src/abuse-protection/types.ts +97 -0
- package/src/abuse-protection/user-agent.ts +124 -0
- package/src/abuse-protection/user-suspension.ts +231 -0
- package/src/ai-temp-auth.ts +315 -0
- package/src/api-proxy-guard.ts +965 -0
- package/src/app-url.ts +96 -0
- package/src/avatar-url.ts +64 -0
- package/src/break-duration.ts +84 -0
- package/src/calendar-auth-token.test.ts +37 -0
- package/src/calendar-auth-token.ts +19 -0
- package/src/calendar-sync-coordination.md +197 -0
- package/src/calendar-utils.test.ts +169 -0
- package/src/calendar-utils.ts +91 -0
- package/src/color-helper.ts +110 -0
- package/src/common/nextjs.tsx +99 -0
- package/src/common/scan.tsx +15 -0
- package/src/configs/reports.ts +160 -0
- package/src/constants.ts +85 -0
- package/src/crypto.ts +21 -0
- package/src/currencies.ts +97 -0
- package/src/date-helper.ts +313 -0
- package/src/editor/convert-to-task.ts +264 -0
- package/src/editor/index.ts +5 -0
- package/src/email/__tests__/client.test.ts +141 -0
- package/src/email/__tests__/validation.test.ts +46 -0
- package/src/email/client.ts +92 -0
- package/src/email/server.ts +128 -0
- package/src/email/validation.ts +11 -0
- package/src/encryption/__tests__/calendar-events.test.ts +411 -0
- package/src/encryption/__tests__/configuration.test.ts +114 -0
- package/src/encryption/__tests__/field-encryption.test.ts +232 -0
- package/src/encryption/__tests__/key-generation.test.ts +30 -0
- package/src/encryption/__tests__/performance-edge-cases.test.ts +187 -0
- package/src/encryption/__tests__/test-helpers.ts +22 -0
- package/src/encryption/__tests__/workspace-key-encryption.test.ts +129 -0
- package/src/encryption/encryption-service.ts +343 -0
- package/src/encryption/index.ts +25 -0
- package/src/encryption/types.ts +57 -0
- package/src/exchange-rates.ts +49 -0
- package/src/feature-flags/__tests__/feature-flags.test.ts +302 -0
- package/src/feature-flags/core.ts +322 -0
- package/src/feature-flags/data.ts +16 -0
- package/src/feature-flags/default.ts +18 -0
- package/src/feature-flags/index.ts +7 -0
- package/src/feature-flags/requestable-features.ts +79 -0
- package/src/feature-flags/types.ts +4 -0
- package/src/fetcher.ts +2 -0
- package/src/finance/index.ts +4 -0
- package/src/finance/interest-calculator.ts +456 -0
- package/src/finance/interest-detector.ts +141 -0
- package/src/finance/transform-invoice-results.ts +219 -0
- package/src/finance/wallet-permissions.test.ts +169 -0
- package/src/finance/wallet-permissions.ts +82 -0
- package/src/format.ts +120 -1
- package/src/generated/platform-build-metadata.ts +11 -0
- package/src/hooks/use-platform.ts +64 -0
- package/src/html-sanitizer.ts +155 -0
- package/src/internal-domains.ts +497 -0
- package/src/keyboard-preset.ts +109 -0
- package/src/label-colors.ts +213 -0
- package/src/launchable-apps.test.ts +126 -0
- package/src/launchable-apps.ts +490 -0
- package/src/name-helper.ts +269 -0
- package/src/next-config.test.ts +234 -0
- package/src/next-config.ts +203 -0
- package/src/node-diff.ts +375 -0
- package/src/notification-service.ts +379 -0
- package/src/nova/scores/__tests__/calculate.test.ts +254 -0
- package/src/nova/scores/calculate.ts +132 -0
- package/src/nova/submissions/check-permission.ts +132 -0
- package/src/onboarding-helper.ts +213 -0
- package/src/path-helper.ts +93 -0
- package/src/permissions.tsx +1170 -0
- package/src/plan-helpers.test.ts +188 -0
- package/src/plan-helpers.ts +80 -0
- package/src/platform-release.test.ts +74 -0
- package/src/platform-release.ts +155 -0
- package/src/portless.ts +124 -0
- package/src/priority-styles.ts +42 -0
- package/src/request-emoji-limit.ts +335 -0
- package/src/search-helper.ts +18 -0
- package/src/search.test.ts +89 -0
- package/src/search.ts +355 -0
- package/src/storage-display-name.ts +30 -0
- package/src/storage-path.ts +147 -0
- package/src/tag-utils.ts +159 -0
- package/src/task/reorder.ts +245 -0
- package/src/task/transformers.ts +149 -0
- package/src/task-date-timezone.ts +133 -0
- package/src/task-description-content.ts +240 -0
- package/src/task-helper/board.ts +193 -0
- package/src/task-helper/bulk-actions.ts +564 -0
- package/src/task-helper/personal-external-staging.ts +21 -0
- package/src/task-helper/recycle-bin.ts +202 -0
- package/src/task-helper/relationships.ts +346 -0
- package/src/task-helper/shared.ts +109 -0
- package/src/task-helper/sort-keys.ts +337 -0
- package/src/task-helper/task-hooks-basic.ts +342 -0
- package/src/task-helper/task-hooks-move.ts +264 -0
- package/src/task-helper/task-operations.ts +278 -0
- package/src/task-helper.ts +12 -0
- package/src/task-helpers.ts +241 -0
- package/src/task-list-status.ts +62 -0
- package/src/task-overrides.ts +82 -0
- package/src/task-snapshot.ts +374 -0
- package/src/text-diff.ts +81 -0
- package/src/text-helper.ts +537 -0
- package/src/time-helper.ts +63 -0
- package/src/time-tracker-period.ts +73 -0
- package/src/timeblock-helper.ts +418 -0
- package/src/timezone.ts +190 -0
- package/src/timezones.json +1271 -0
- package/src/upstash-rest.ts +56 -0
- package/src/user-helper.ts +296 -0
- package/src/uuid-helper.ts +11 -0
- package/src/workspace-handle.ts +10 -0
- package/src/workspace-helper.ts +1408 -0
- package/src/workspace-limits.ts +68 -0
- package/src/yjs-helper.ts +217 -0
- package/src/yjs-task-description.ts +81 -0
- package/tsconfig.json +3 -5
- package/tsconfig.typecheck.json +33 -0
- package/vitest.config.ts +36 -0
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -2
- package/dist/index.mjs.map +0 -1
- package/eslint.config.mjs +0 -20
- package/rollup.config.js +0 -41
- package/src/index.ts +0 -1
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for task management and calculations
|
|
3
|
+
*/
|
|
4
|
+
import type { Active, Over } from '@dnd-kit/core';
|
|
5
|
+
import type { TaskPriority } from '@tuturuuu/types/primitives/Priority';
|
|
6
|
+
|
|
7
|
+
interface Task {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
priority?: TaskPriority | null;
|
|
12
|
+
created_at?: string;
|
|
13
|
+
updated_at?: string;
|
|
14
|
+
end_date?: string | null;
|
|
15
|
+
boardId: string;
|
|
16
|
+
boardName: string;
|
|
17
|
+
listName: string;
|
|
18
|
+
listStatus?: string;
|
|
19
|
+
archived?: boolean;
|
|
20
|
+
completed_at?: string;
|
|
21
|
+
closed_at?: string;
|
|
22
|
+
finished_at?: string;
|
|
23
|
+
done_at?: string;
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface BoardMetrics {
|
|
28
|
+
id: string;
|
|
29
|
+
totalTasks: number;
|
|
30
|
+
completedTasks: number;
|
|
31
|
+
overdueTasks: number;
|
|
32
|
+
highPriorityTasks: number;
|
|
33
|
+
progressPercentage: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface DraggableData {
|
|
37
|
+
type: 'Task' | 'Column';
|
|
38
|
+
task?: unknown;
|
|
39
|
+
column?: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function hasDraggableData(
|
|
43
|
+
element: Active | Over | null
|
|
44
|
+
): element is Active | Over {
|
|
45
|
+
if (!element) return false;
|
|
46
|
+
if (!element.data?.current) return false;
|
|
47
|
+
|
|
48
|
+
const data = element.data.current as DraggableData;
|
|
49
|
+
|
|
50
|
+
if (data.type === 'Task' && !data.task) return false;
|
|
51
|
+
if (data.type === 'Column' && !data.column) return false;
|
|
52
|
+
|
|
53
|
+
return data.type === 'Task' || data.type === 'Column';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Calculate the number of days a task is overdue
|
|
58
|
+
* @param dueDate The task's due date
|
|
59
|
+
* @returns Number of days the task is overdue
|
|
60
|
+
*/
|
|
61
|
+
export function calculateOverdueDays(dueDate: string | Date): number {
|
|
62
|
+
const due = new Date(dueDate);
|
|
63
|
+
return Math.ceil((Date.now() - due.getTime()) / (1000 * 60 * 60 * 24));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Safe utility to get completion date from various possible fields
|
|
68
|
+
* @param task Task object with potential completion date fields
|
|
69
|
+
* @returns Date object or null if no valid completion date found
|
|
70
|
+
*/
|
|
71
|
+
export function getTaskCompletionDate(task: Task): Date | null {
|
|
72
|
+
const possibleFields = [
|
|
73
|
+
'updated_at',
|
|
74
|
+
'completed_at',
|
|
75
|
+
'closed_at',
|
|
76
|
+
'finished_at',
|
|
77
|
+
'done_at',
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
// First try to get explicit completion dates
|
|
81
|
+
for (const field of possibleFields) {
|
|
82
|
+
const dateStr = task[field];
|
|
83
|
+
if (dateStr) {
|
|
84
|
+
const date = new Date(dateStr as string);
|
|
85
|
+
if (!Number.isNaN(date.getTime()) && date.getTime() > 0) {
|
|
86
|
+
return date;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Fallback: if task is completed but no completion date, use updated_at or created_at as estimate
|
|
92
|
+
if (
|
|
93
|
+
task.listStatus === 'done' ||
|
|
94
|
+
task.listStatus === 'closed' ||
|
|
95
|
+
task.archived
|
|
96
|
+
) {
|
|
97
|
+
// Try updated_at first (might indicate when status was changed)
|
|
98
|
+
if (task.updated_at) {
|
|
99
|
+
const date = new Date(task.updated_at);
|
|
100
|
+
if (!Number.isNaN(date.getTime()) && date.getTime() > 0) {
|
|
101
|
+
return date;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Last resort: use creation date (not ideal but better than nothing)
|
|
106
|
+
if (task.created_at) {
|
|
107
|
+
const date = new Date(task.created_at);
|
|
108
|
+
if (!Number.isNaN(date.getTime()) && date.getTime() > 0) {
|
|
109
|
+
return date;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get status color for task visualization
|
|
119
|
+
* @param status Task status string
|
|
120
|
+
* @returns CSS class name for background color
|
|
121
|
+
*/
|
|
122
|
+
export function getStatusColor(status: string): string {
|
|
123
|
+
switch (status) {
|
|
124
|
+
case 'done':
|
|
125
|
+
case 'closed':
|
|
126
|
+
return 'bg-green-500';
|
|
127
|
+
case 'review':
|
|
128
|
+
return 'bg-orange-500';
|
|
129
|
+
case 'active':
|
|
130
|
+
return 'bg-blue-500';
|
|
131
|
+
default:
|
|
132
|
+
return 'bg-gray-400';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Filter tasks based on board selection and status
|
|
138
|
+
* @param tasks Array of tasks to filter
|
|
139
|
+
* @param boardId Board ID to filter by (null for all boards)
|
|
140
|
+
* @param statusFilter Status filter to apply
|
|
141
|
+
* @returns Filtered array of tasks
|
|
142
|
+
*/
|
|
143
|
+
export function filterTasks(
|
|
144
|
+
tasks: Task[],
|
|
145
|
+
boardId: string | null,
|
|
146
|
+
statusFilter: 'all' | 'not_started' | 'active' | 'review' | 'done' | 'closed'
|
|
147
|
+
): Task[] {
|
|
148
|
+
let filtered = tasks;
|
|
149
|
+
|
|
150
|
+
// Filter by board
|
|
151
|
+
if (boardId) {
|
|
152
|
+
filtered = filtered.filter((task) => task.boardId === boardId);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Filter by status
|
|
156
|
+
if (statusFilter !== 'all') {
|
|
157
|
+
filtered = filtered.filter((task) => {
|
|
158
|
+
const taskStatus = task.listStatus || 'not_started';
|
|
159
|
+
return taskStatus === statusFilter;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return filtered;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Group tasks by their status
|
|
168
|
+
* @param tasks Array of tasks to group
|
|
169
|
+
* @returns Object with tasks grouped by status
|
|
170
|
+
*/
|
|
171
|
+
export function groupTasksByStatus(tasks: Task[]): Record<string, Task[]> {
|
|
172
|
+
const groups: Record<string, Task[]> = {
|
|
173
|
+
not_started: [],
|
|
174
|
+
active: [],
|
|
175
|
+
done: [],
|
|
176
|
+
closed: [],
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
tasks.forEach((task) => {
|
|
180
|
+
if (task.archived || task.listStatus === 'done') {
|
|
181
|
+
groups.done?.push(task);
|
|
182
|
+
} else if (task.listStatus === 'closed') {
|
|
183
|
+
groups.closed?.push(task);
|
|
184
|
+
} else if (task.listStatus === 'active') {
|
|
185
|
+
groups.active?.push(task);
|
|
186
|
+
} else {
|
|
187
|
+
groups.not_started?.push(task);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return groups;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Calculate filtered metrics for boards based on selection
|
|
196
|
+
* @param data Array of board data
|
|
197
|
+
* @param selectedBoard Board ID to filter by (null for all boards)
|
|
198
|
+
* @returns Aggregated metrics
|
|
199
|
+
*/
|
|
200
|
+
export function getFilteredMetrics(
|
|
201
|
+
data: BoardMetrics[],
|
|
202
|
+
selectedBoard: string | null
|
|
203
|
+
) {
|
|
204
|
+
const filteredData = selectedBoard
|
|
205
|
+
? data.filter((board) => board.id === selectedBoard)
|
|
206
|
+
: data;
|
|
207
|
+
|
|
208
|
+
const totalTasks = filteredData.reduce(
|
|
209
|
+
(sum, board) => sum + board.totalTasks,
|
|
210
|
+
0
|
|
211
|
+
);
|
|
212
|
+
const totalCompleted = filteredData.reduce(
|
|
213
|
+
(sum, board) => sum + board.completedTasks,
|
|
214
|
+
0
|
|
215
|
+
);
|
|
216
|
+
const totalOverdue = filteredData.reduce(
|
|
217
|
+
(sum, board) => sum + board.overdueTasks,
|
|
218
|
+
0
|
|
219
|
+
);
|
|
220
|
+
const totalHighPriority = filteredData.reduce(
|
|
221
|
+
(sum, board) => sum + board.highPriorityTasks,
|
|
222
|
+
0
|
|
223
|
+
);
|
|
224
|
+
const avgProgress =
|
|
225
|
+
filteredData.length > 0
|
|
226
|
+
? Math.round(
|
|
227
|
+
filteredData.reduce(
|
|
228
|
+
(sum, board) => sum + board.progressPercentage,
|
|
229
|
+
0
|
|
230
|
+
) / filteredData.length
|
|
231
|
+
)
|
|
232
|
+
: 0;
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
totalTasks,
|
|
236
|
+
totalCompleted,
|
|
237
|
+
totalOverdue,
|
|
238
|
+
totalHighPriority,
|
|
239
|
+
avgProgress,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { SupportedColor } from '@tuturuuu/types/primitives/SupportedColors';
|
|
2
|
+
import type { TaskBoardStatus } from '@tuturuuu/types/primitives/TaskBoard';
|
|
3
|
+
|
|
4
|
+
export const TASK_BOARD_STATUSES = [
|
|
5
|
+
'documents',
|
|
6
|
+
'not_started',
|
|
7
|
+
'active',
|
|
8
|
+
'review',
|
|
9
|
+
'done',
|
|
10
|
+
'closed',
|
|
11
|
+
] as const satisfies readonly TaskBoardStatus[];
|
|
12
|
+
|
|
13
|
+
export const TASK_BOARD_WORKFLOW_STATUSES = [
|
|
14
|
+
'not_started',
|
|
15
|
+
'active',
|
|
16
|
+
'review',
|
|
17
|
+
'done',
|
|
18
|
+
'closed',
|
|
19
|
+
] as const satisfies readonly TaskBoardStatus[];
|
|
20
|
+
|
|
21
|
+
export function isTaskBoardStatus(
|
|
22
|
+
status: string | null | undefined
|
|
23
|
+
): status is TaskBoardStatus {
|
|
24
|
+
return TASK_BOARD_STATUSES.includes(status as TaskBoardStatus);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function isTaskBoardCompletedStatus(status: string | null | undefined) {
|
|
28
|
+
return status === 'done';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function isTaskBoardResolvedStatus(status: string | null | undefined) {
|
|
32
|
+
return (
|
|
33
|
+
status === 'review' ||
|
|
34
|
+
isTaskBoardCompletedStatus(status) ||
|
|
35
|
+
status === 'closed'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function isTaskBoardTerminalStatus(status: string | null | undefined) {
|
|
40
|
+
return status === 'done' || status === 'closed';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getDefaultTaskListColorForStatus(
|
|
44
|
+
status: string | null | undefined
|
|
45
|
+
): SupportedColor {
|
|
46
|
+
switch (status) {
|
|
47
|
+
case 'documents':
|
|
48
|
+
return 'CYAN';
|
|
49
|
+
case 'not_started':
|
|
50
|
+
return 'GRAY';
|
|
51
|
+
case 'active':
|
|
52
|
+
return 'BLUE';
|
|
53
|
+
case 'review':
|
|
54
|
+
return 'ORANGE';
|
|
55
|
+
case 'done':
|
|
56
|
+
return 'GREEN';
|
|
57
|
+
case 'closed':
|
|
58
|
+
return 'PURPLE';
|
|
59
|
+
default:
|
|
60
|
+
return 'GRAY';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TaskUserOverride,
|
|
3
|
+
TaskWithRelations,
|
|
4
|
+
UserBoardListOverride,
|
|
5
|
+
} from '@tuturuuu/types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* When self_managed is true, applies personal overrides to task fields.
|
|
9
|
+
* Returns a new task object with effective values; never mutates the original.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveEffectiveValues(
|
|
12
|
+
task: TaskWithRelations,
|
|
13
|
+
overrides: TaskUserOverride | null | undefined
|
|
14
|
+
): TaskWithRelations {
|
|
15
|
+
if (!overrides?.self_managed) return task;
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
...task,
|
|
19
|
+
priority:
|
|
20
|
+
overrides.priority_override !== null
|
|
21
|
+
? overrides.priority_override
|
|
22
|
+
: task.priority,
|
|
23
|
+
end_date:
|
|
24
|
+
overrides.due_date_override !== null
|
|
25
|
+
? overrides.due_date_override
|
|
26
|
+
: task.end_date,
|
|
27
|
+
estimation_points:
|
|
28
|
+
overrides.estimation_override !== null
|
|
29
|
+
? overrides.estimation_override
|
|
30
|
+
: task.estimation_points,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Checks whether a task should be hidden from My Tasks based on:
|
|
36
|
+
* 1. Personal completion (completed_at is set)
|
|
37
|
+
* 2. Personal unassignment (personally_unassigned is true)
|
|
38
|
+
* 3. Board/list override status being 'done' or 'closed'
|
|
39
|
+
*/
|
|
40
|
+
export function isPersonallyHidden(
|
|
41
|
+
task: TaskWithRelations,
|
|
42
|
+
overrides: TaskUserOverride | null | undefined,
|
|
43
|
+
boardListOverrides: UserBoardListOverride[]
|
|
44
|
+
): boolean {
|
|
45
|
+
// Check personal completion
|
|
46
|
+
if (overrides?.completed_at) return true;
|
|
47
|
+
|
|
48
|
+
// Check personal unassignment
|
|
49
|
+
if (overrides?.personally_unassigned) return true;
|
|
50
|
+
|
|
51
|
+
// Check board-level override
|
|
52
|
+
const boardId = task.list?.board?.id;
|
|
53
|
+
if (boardId) {
|
|
54
|
+
const boardOverride = boardListOverrides.find(
|
|
55
|
+
(o) => o.scope_type === 'board' && o.board_id === boardId
|
|
56
|
+
);
|
|
57
|
+
if (
|
|
58
|
+
boardOverride &&
|
|
59
|
+
(boardOverride.personal_status === 'done' ||
|
|
60
|
+
boardOverride.personal_status === 'closed')
|
|
61
|
+
) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check list-level override
|
|
67
|
+
const listId = task.list?.id;
|
|
68
|
+
if (listId) {
|
|
69
|
+
const listOverride = boardListOverrides.find(
|
|
70
|
+
(o) => o.scope_type === 'list' && o.list_id === listId
|
|
71
|
+
);
|
|
72
|
+
if (
|
|
73
|
+
listOverride &&
|
|
74
|
+
(listOverride.personal_status === 'done' ||
|
|
75
|
+
listOverride.personal_status === 'closed')
|
|
76
|
+
) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return false;
|
|
82
|
+
}
|