@anchrd/intel-ui 0.16.0 → 0.17.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 +10 -2
- package/src/agent/agent-profile/agent-profile.tsx +2 -0
- package/src/board/board-calendar/board-calendar.tsx +89 -0
- package/src/board/board-card/board-card.tsx +106 -0
- package/src/board/board-data/board-data.ts +241 -0
- package/src/board/board-data/board-data.types.ts +63 -0
- package/src/board/board-detail/board-detail.tsx +629 -0
- package/src/board/board-gantt/board-gantt.ts +545 -0
- package/src/board/board-gantt/board-gantt.tsx +286 -0
- package/src/board/board-graph/board-graph.ts +174 -0
- package/src/board/board-graph/board-graph.tsx +168 -0
- package/src/board/board-items/board-items.ts +183 -0
- package/src/board/board-kanban/board-kanban.ts +97 -0
- package/src/board/board-kanban/board-kanban.tsx +211 -0
- package/src/board/board-status/board-status.ts +59 -0
- package/src/board/board-statuses/board-statuses.ts +63 -0
- package/src/board/board-statuses/board-statuses.tsx +228 -0
- package/src/board/board-table/board-table.ts +33 -0
- package/src/board/board-table/board-table.tsx +413 -0
- package/src/board/board-views/board-views.tsx +68 -0
- package/src/board/board-views/board-views.types.ts +29 -0
- package/src/board/board.tsx +251 -0
- package/src/components/ui/dropdown-menu.tsx +25 -0
- package/src/components/ui/item-calendar.tsx +181 -0
- package/src/components/ui/item-gantt.tsx +463 -0
- package/src/components/ui/kanban.tsx +245 -0
- package/src/components/ui/switch.tsx +25 -0
- package/src/data/intel-data-provider/intel-data-provider.ts +52 -0
- package/src/data/intel-data-provider/intel-data-provider.types.ts +31 -0
- package/src/i18n/de.json +88 -1
- package/src/i18n/en.json +88 -1
- package/src/i18n/es.json +88 -1
- package/src/kind-icon.ts +12 -1
- package/src/nodes/nodes.tsx +16 -0
- package/src/styles.css +33 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Announcements,
|
|
3
|
+
closestCorners,
|
|
4
|
+
DndContext,
|
|
5
|
+
DragOverlay,
|
|
6
|
+
type DragStartEvent,
|
|
7
|
+
KeyboardSensor,
|
|
8
|
+
PointerSensor,
|
|
9
|
+
useDroppable,
|
|
10
|
+
useSensor,
|
|
11
|
+
useSensors,
|
|
12
|
+
} from "@dnd-kit/core";
|
|
13
|
+
import { SortableContext, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
14
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
15
|
+
import { GripVertical } from "lucide-react";
|
|
16
|
+
import { type ReactNode, useState } from "react";
|
|
17
|
+
import { createPortal } from "react-dom";
|
|
18
|
+
import { cn } from "@/lib/utils";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A kanban board on dnd-kit, from the Kibo UI registry (MIT) and changed where it had to be.
|
|
22
|
+
*
|
|
23
|
+
* Registry code is our code, and four things about the original do not survive contact with a board
|
|
24
|
+
* whose truth is on a server:
|
|
25
|
+
*
|
|
26
|
+
* ⚠️ **It reported a drop as a new ARRAY, and it reported one twice.** The original called
|
|
27
|
+
* `onDataChange` in `onDragOver` for a cross-column move and again in `onDragEnd`, and both times
|
|
28
|
+
* with a reordered copy of the whole list. Against a server that is two writes for one drag — and
|
|
29
|
+
* the second one describes a state the first already changed. This version says nothing while the
|
|
30
|
+
* card is in the air and emits ONE drop at the end, naming what was dropped and what it was dropped
|
|
31
|
+
* on. What that means is the caller's to decide (`board-kanban.ts`), which is also where the single
|
|
32
|
+
* `board_task_move` is sent from.
|
|
33
|
+
*
|
|
34
|
+
* ⚠️ **It mutated its own props** — `newData[activeIndex].column = overColumn` writes into the
|
|
35
|
+
* object the caller passed in. With TanStack Query holding that object, a drag edited the cache in
|
|
36
|
+
* place and no re-render followed.
|
|
37
|
+
*
|
|
38
|
+
* ⚠️ **`tunnel-rat` is gone.** It existed to render the dragged card into the overlay from inside
|
|
39
|
+
* the card component; `DragOverlay` already does that, and the caller draws the overlay itself.
|
|
40
|
+
* One dependency fewer for something React has a portal for.
|
|
41
|
+
*
|
|
42
|
+
* ⚠️ **The screen reader announcements are handed in.** The originals are English string literals
|
|
43
|
+
* in the component; Intel speaks three languages and its text comes from `useI18n()`.
|
|
44
|
+
*
|
|
45
|
+
* ⚠️ **The card is not itself a `role="button"`.** The original spread `useSortable().attributes`
|
|
46
|
+
* onto the card, which contributes `role="button"` and `tabIndex={0}` — and a card that also has to
|
|
47
|
+
* be OPENED then holds a real button inside that one. Two tab stops computing the same accessible
|
|
48
|
+
* name from the same contents, and an Enter that means "start dragging" on the outside and "open
|
|
49
|
+
* this" on the inside. Split instead: the card keeps `listeners`, so a pointer can still drag it
|
|
50
|
+
* from anywhere, and `attributes` moves to a handle that is the one tab stop for dragging and says
|
|
51
|
+
* so.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
export interface KanbanColumn {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface KanbanDrop {
|
|
60
|
+
activeId: string;
|
|
61
|
+
// The column or the card it was let go over. `null` means it was let go over nothing — a drag
|
|
62
|
+
// that ended outside the board, which is a cancel and not a move.
|
|
63
|
+
overId: string | null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function KanbanBoard({
|
|
67
|
+
id,
|
|
68
|
+
droppable = true,
|
|
69
|
+
children,
|
|
70
|
+
className,
|
|
71
|
+
}: {
|
|
72
|
+
id: string;
|
|
73
|
+
// A column that exists to be READ but must not be written into. It still draws and still holds
|
|
74
|
+
// cards; it simply never becomes a drop target, so nothing can be dropped where the answer would
|
|
75
|
+
// be a refusal (`board-kanban.tsx`).
|
|
76
|
+
droppable?: boolean;
|
|
77
|
+
children: ReactNode;
|
|
78
|
+
className?: string;
|
|
79
|
+
}) {
|
|
80
|
+
const { isOver, setNodeRef } = useDroppable({ id, disabled: !droppable });
|
|
81
|
+
return (
|
|
82
|
+
<div
|
|
83
|
+
ref={setNodeRef}
|
|
84
|
+
data-slot="kanban-column"
|
|
85
|
+
data-droppable={droppable}
|
|
86
|
+
className={cn(
|
|
87
|
+
"flex size-full min-h-40 flex-col divide-y overflow-hidden rounded-lg border bg-muted/40 text-xs ring-2 transition-colors",
|
|
88
|
+
isOver ? "ring-ring" : "ring-transparent",
|
|
89
|
+
className,
|
|
90
|
+
)}
|
|
91
|
+
>
|
|
92
|
+
{children}
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function KanbanHeader({ children }: { children: ReactNode }) {
|
|
98
|
+
return <div className="flex items-center gap-2 p-2 text-sm font-semibold">{children}</div>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function KanbanCards({ id, children }: { id: string; children: ReactNode }) {
|
|
102
|
+
return (
|
|
103
|
+
<div
|
|
104
|
+
// The column scrolls, not the page: four lanes of different lengths must not make the board
|
|
105
|
+
// as tall as its longest one.
|
|
106
|
+
className="flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto p-2"
|
|
107
|
+
data-slot="kanban-cards"
|
|
108
|
+
data-column={id}
|
|
109
|
+
>
|
|
110
|
+
{children}
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function KanbanCard({
|
|
116
|
+
id,
|
|
117
|
+
dragLabel,
|
|
118
|
+
children,
|
|
119
|
+
className,
|
|
120
|
+
}: {
|
|
121
|
+
id: string;
|
|
122
|
+
// What the drag handle is called. Handed in because only the caller knows what the card is of.
|
|
123
|
+
dragLabel: string;
|
|
124
|
+
children: ReactNode;
|
|
125
|
+
className?: string;
|
|
126
|
+
}) {
|
|
127
|
+
const {
|
|
128
|
+
attributes,
|
|
129
|
+
listeners,
|
|
130
|
+
setNodeRef,
|
|
131
|
+
setActivatorNodeRef,
|
|
132
|
+
transition,
|
|
133
|
+
transform,
|
|
134
|
+
isDragging,
|
|
135
|
+
} = useSortable({ id });
|
|
136
|
+
return (
|
|
137
|
+
<div
|
|
138
|
+
ref={setNodeRef}
|
|
139
|
+
style={{ transition, transform: CSS.Transform.toString(transform) }}
|
|
140
|
+
// ⚠️ `listeners` here, `attributes` NOT. The pointer sensor has to see a press anywhere on the
|
|
141
|
+
// card — a kanban whose cards can only be dragged by a grip is a kanban nobody drags — but
|
|
142
|
+
// `attributes` is what carries `role="button"` and `tabIndex`, and those on the card would
|
|
143
|
+
// wrap the control that OPENS it in a second button. Pointer drag from the whole card,
|
|
144
|
+
// keyboard drag from the handle, one tab stop each.
|
|
145
|
+
//
|
|
146
|
+
// ⚠️ The split only holds because the handle registers itself as the ACTIVATOR below. Without
|
|
147
|
+
// that, `listeners` on this element make every Enter and Space inside the card a drag: the
|
|
148
|
+
// key handler bubbles up from whatever is focused, `KeyboardSensor` accepts it and calls
|
|
149
|
+
// `preventDefault()`, and the button that opens the task never sees its own activation.
|
|
150
|
+
{...listeners}
|
|
151
|
+
className={cn(
|
|
152
|
+
"flex cursor-grab items-start gap-1 rounded-lg border bg-card p-3 text-left shadow-sm",
|
|
153
|
+
// Left where it was, faded, rather than removed: a lane that reflows while a card is over
|
|
154
|
+
// it moves the drop target out from under the pointer.
|
|
155
|
+
isDragging && "opacity-40",
|
|
156
|
+
className,
|
|
157
|
+
)}
|
|
158
|
+
>
|
|
159
|
+
{/* The keyboard half of the drag: `attributes` puts the card into the tab order exactly once,
|
|
160
|
+
here, where it has a name of its own that says what it does.
|
|
161
|
+
|
|
162
|
+
⚠️ `setActivatorNodeRef` is what makes that true rather than merely intended. dnd-kit's
|
|
163
|
+
`KeyboardSensor` opens with `if (activator && event.target !== activator) return false` —
|
|
164
|
+
a guard that only exists once something has claimed to BE the activator. Unclaimed, the
|
|
165
|
+
keyboard sensor accepts a key press from anywhere under the listeners, so Enter on the
|
|
166
|
+
card's own "open" button started a drag and swallowed the click. The pointer drag is
|
|
167
|
+
unaffected: `PointerSensor` never consults the activator node. */}
|
|
168
|
+
<button
|
|
169
|
+
type="button"
|
|
170
|
+
ref={setActivatorNodeRef}
|
|
171
|
+
aria-label={dragLabel}
|
|
172
|
+
{...listeners}
|
|
173
|
+
{...attributes}
|
|
174
|
+
className="mt-0.5 shrink-0 cursor-grab rounded text-muted-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
175
|
+
>
|
|
176
|
+
<GripVertical aria-hidden="true" className="size-4" />
|
|
177
|
+
</button>
|
|
178
|
+
<div className="min-w-0 flex-1">{children}</div>
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function KanbanProvider({
|
|
184
|
+
columns,
|
|
185
|
+
itemsByColumn,
|
|
186
|
+
children,
|
|
187
|
+
announcements,
|
|
188
|
+
overlay,
|
|
189
|
+
onDrop,
|
|
190
|
+
className,
|
|
191
|
+
}: {
|
|
192
|
+
columns: KanbanColumn[];
|
|
193
|
+
// Which cards stand in which column, in the order they are drawn. The provider needs it for the
|
|
194
|
+
// sortable contexts and asks for it rather than deriving it, so the caller stays the only place
|
|
195
|
+
// that knows how a board is ordered.
|
|
196
|
+
itemsByColumn: Map<string, string[]>;
|
|
197
|
+
children: (column: KanbanColumn) => ReactNode;
|
|
198
|
+
announcements: Announcements;
|
|
199
|
+
overlay(activeId: string): ReactNode;
|
|
200
|
+
onDrop(drop: KanbanDrop): void;
|
|
201
|
+
className?: string;
|
|
202
|
+
}) {
|
|
203
|
+
const [activeId, setActiveId] = useState<string | null>(null);
|
|
204
|
+
const sensors = useSensors(
|
|
205
|
+
// ⚠️ A distance before a drag starts, and it is not decoration: without it every click on a
|
|
206
|
+
// card is a drag of zero pixels, and the card can then no longer be opened by clicking it.
|
|
207
|
+
useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
208
|
+
useSensor(KeyboardSensor),
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<DndContext
|
|
213
|
+
accessibility={{ announcements }}
|
|
214
|
+
// Corners rather than centres: a tall card over a short one wins on centre distance even
|
|
215
|
+
// when the pointer is nowhere near it.
|
|
216
|
+
collisionDetection={closestCorners}
|
|
217
|
+
sensors={sensors}
|
|
218
|
+
onDragStart={(event: DragStartEvent) => setActiveId(String(event.active.id))}
|
|
219
|
+
onDragCancel={() => setActiveId(null)}
|
|
220
|
+
onDragEnd={(event) => {
|
|
221
|
+
setActiveId(null);
|
|
222
|
+
const over = event.over === null ? null : String(event.over.id);
|
|
223
|
+
onDrop({ activeId: String(event.active.id), overId: over });
|
|
224
|
+
}}
|
|
225
|
+
>
|
|
226
|
+
<div className={cn("grid size-full auto-cols-fr grid-flow-col gap-3", className)}>
|
|
227
|
+
{columns.map((column) => (
|
|
228
|
+
<SortableContext
|
|
229
|
+
key={column.id}
|
|
230
|
+
items={itemsByColumn.get(column.id) ?? []}
|
|
231
|
+
strategy={verticalListSortingStrategy}
|
|
232
|
+
>
|
|
233
|
+
{children(column)}
|
|
234
|
+
</SortableContext>
|
|
235
|
+
))}
|
|
236
|
+
</div>
|
|
237
|
+
{typeof document === "undefined"
|
|
238
|
+
? null
|
|
239
|
+
: createPortal(
|
|
240
|
+
<DragOverlay>{activeId === null ? null : overlay(activeId)}</DragOverlay>,
|
|
241
|
+
document.body,
|
|
242
|
+
)}
|
|
243
|
+
</DndContext>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Switch as SwitchPrimitive } from "radix-ui";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
function Switch({ className, ...props }: React.ComponentProps<typeof SwitchPrimitive.Root>) {
|
|
6
|
+
return (
|
|
7
|
+
<SwitchPrimitive.Root
|
|
8
|
+
data-slot="switch"
|
|
9
|
+
className={cn(
|
|
10
|
+
"peer inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
|
11
|
+
className,
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<SwitchPrimitive.Thumb
|
|
16
|
+
data-slot="switch-thumb"
|
|
17
|
+
className={cn(
|
|
18
|
+
"pointer-events-none block size-4 rounded-full bg-background ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0",
|
|
19
|
+
)}
|
|
20
|
+
/>
|
|
21
|
+
</SwitchPrimitive.Root>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { Switch };
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AddBoardTaskInput,
|
|
2
3
|
AgentCosts,
|
|
3
4
|
AgentKeyRotated,
|
|
4
5
|
AppendTableRowsInput,
|
|
5
6
|
AppendTableRowsResult,
|
|
6
7
|
ArchiveFlowInput,
|
|
7
8
|
ArchiveNodeInput,
|
|
9
|
+
BoardTaskResult,
|
|
8
10
|
BundleImportResult,
|
|
9
11
|
CompleteFlowRunStepInput,
|
|
12
|
+
ConfigureBoardInput,
|
|
13
|
+
ConfigureBoardResult,
|
|
10
14
|
CreateAgentInput,
|
|
11
15
|
CreatedAgent,
|
|
12
16
|
CreateFlowInput,
|
|
13
17
|
CreateNodeInput,
|
|
14
18
|
DefineTableInput,
|
|
19
|
+
DeleteBoardTaskInput,
|
|
20
|
+
DeleteBoardTaskResult,
|
|
15
21
|
Flow,
|
|
16
22
|
FlowDocument,
|
|
17
23
|
FlowList,
|
|
@@ -26,8 +32,10 @@ import {
|
|
|
26
32
|
ListFlowsInput,
|
|
27
33
|
ListNodesInput,
|
|
28
34
|
ModelCatalog,
|
|
35
|
+
MoveBoardTaskInput,
|
|
29
36
|
Node,
|
|
30
37
|
NodeAgent,
|
|
38
|
+
NodeBoard,
|
|
31
39
|
NodeDocument,
|
|
32
40
|
NodeGraph,
|
|
33
41
|
NodeLinkList,
|
|
@@ -56,6 +64,7 @@ import {
|
|
|
56
64
|
StartFlowRunInput,
|
|
57
65
|
ToolCatalog,
|
|
58
66
|
ToolServerCatalog,
|
|
67
|
+
UpdateBoardTaskInput,
|
|
59
68
|
UpdateFlowInput,
|
|
60
69
|
UpdateNodeInput,
|
|
61
70
|
} from "@anchrd/intel-contract";
|
|
@@ -327,6 +336,49 @@ export function createIntelDataProvider(
|
|
|
327
336
|
{ method: "POST", body: JSON.stringify(parsed) },
|
|
328
337
|
);
|
|
329
338
|
},
|
|
339
|
+
async getBoard(nodeId) {
|
|
340
|
+
return await request(`/nodes/${encodeURIComponent(nodeId)}/board`, NodeBoard);
|
|
341
|
+
},
|
|
342
|
+
async configureBoard(input) {
|
|
343
|
+
const parsed = ConfigureBoardInput.parse(input);
|
|
344
|
+
return await request(
|
|
345
|
+
`/nodes/${encodeURIComponent(parsed.nodeId)}/board/configure`,
|
|
346
|
+
ConfigureBoardResult,
|
|
347
|
+
{ method: "POST", body: JSON.stringify(parsed) },
|
|
348
|
+
);
|
|
349
|
+
},
|
|
350
|
+
async addBoardTask(input) {
|
|
351
|
+
const parsed = AddBoardTaskInput.parse(input);
|
|
352
|
+
return await request(
|
|
353
|
+
`/nodes/${encodeURIComponent(parsed.nodeId)}/board/tasks`,
|
|
354
|
+
BoardTaskResult,
|
|
355
|
+
{ method: "POST", body: JSON.stringify(parsed) },
|
|
356
|
+
);
|
|
357
|
+
},
|
|
358
|
+
async updateBoardTask(input) {
|
|
359
|
+
const parsed = UpdateBoardTaskInput.parse(input);
|
|
360
|
+
return await request(
|
|
361
|
+
`/nodes/${encodeURIComponent(parsed.nodeId)}/board/tasks/update`,
|
|
362
|
+
BoardTaskResult,
|
|
363
|
+
{ method: "POST", body: JSON.stringify(parsed) },
|
|
364
|
+
);
|
|
365
|
+
},
|
|
366
|
+
async moveBoardTask(input) {
|
|
367
|
+
const parsed = MoveBoardTaskInput.parse(input);
|
|
368
|
+
return await request(
|
|
369
|
+
`/nodes/${encodeURIComponent(parsed.nodeId)}/board/tasks/move`,
|
|
370
|
+
BoardTaskResult,
|
|
371
|
+
{ method: "POST", body: JSON.stringify(parsed) },
|
|
372
|
+
);
|
|
373
|
+
},
|
|
374
|
+
async deleteBoardTask(input) {
|
|
375
|
+
const parsed = DeleteBoardTaskInput.parse(input);
|
|
376
|
+
return await request(
|
|
377
|
+
`/nodes/${encodeURIComponent(parsed.nodeId)}/board/tasks/delete`,
|
|
378
|
+
DeleteBoardTaskResult,
|
|
379
|
+
{ method: "POST", body: JSON.stringify(parsed) },
|
|
380
|
+
);
|
|
381
|
+
},
|
|
330
382
|
async listNodeVersions(nodeId) {
|
|
331
383
|
return await request(`/nodes/${encodeURIComponent(nodeId)}/versions`, NodeVersionList);
|
|
332
384
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
AddBoardTaskInput,
|
|
2
3
|
AgentCosts,
|
|
3
4
|
AgentKeyRotated,
|
|
4
5
|
AgentScheduleTarget,
|
|
@@ -6,13 +7,18 @@ import type {
|
|
|
6
7
|
AppendTableRowsResult,
|
|
7
8
|
ArchiveFlowInput,
|
|
8
9
|
ArchiveNodeInput,
|
|
10
|
+
BoardTaskResult,
|
|
9
11
|
BundleImportResult,
|
|
10
12
|
CompleteFlowRunStepInput,
|
|
13
|
+
ConfigureBoardInput,
|
|
14
|
+
ConfigureBoardResult,
|
|
11
15
|
CreateAgentInput,
|
|
12
16
|
CreatedAgent,
|
|
13
17
|
CreateFlowInput,
|
|
14
18
|
CreateNodeInput,
|
|
15
19
|
DefineTableInput,
|
|
20
|
+
DeleteBoardTaskInput,
|
|
21
|
+
DeleteBoardTaskResult,
|
|
16
22
|
Flow,
|
|
17
23
|
FlowDocument,
|
|
18
24
|
FlowList,
|
|
@@ -27,8 +33,10 @@ import type {
|
|
|
27
33
|
ListFlowsInput,
|
|
28
34
|
ListNodesInput,
|
|
29
35
|
ModelCatalog,
|
|
36
|
+
MoveBoardTaskInput,
|
|
30
37
|
Node,
|
|
31
38
|
NodeAgent,
|
|
39
|
+
NodeBoard,
|
|
32
40
|
NodeDocument,
|
|
33
41
|
NodeGraph,
|
|
34
42
|
NodeKind,
|
|
@@ -57,6 +65,7 @@ import type {
|
|
|
57
65
|
StartFlowRunInput,
|
|
58
66
|
ToolCatalog,
|
|
59
67
|
ToolServerCatalog,
|
|
68
|
+
UpdateBoardTaskInput,
|
|
60
69
|
UpdateFlowInput,
|
|
61
70
|
UpdateNodeInput,
|
|
62
71
|
} from "@anchrd/intel-contract";
|
|
@@ -127,6 +136,28 @@ export interface IntelDataProvider {
|
|
|
127
136
|
getNodeTable(nodeId: string): Promise<NodeTable>;
|
|
128
137
|
defineTable(input: DefineTableInput): Promise<NodeTable>;
|
|
129
138
|
appendTableRows(input: AppendTableRowsInput): Promise<AppendTableRowsResult>;
|
|
139
|
+
|
|
140
|
+
// A board's whole document — the status list and every task — as one read (#285). There is no
|
|
141
|
+
// per-task GET and there is not meant to be: the file IS the board, and every view of it
|
|
142
|
+
// (anchrd/intel#286) draws the same list.
|
|
143
|
+
getBoard(nodeId: string): Promise<NodeBoard>;
|
|
144
|
+
// The status list, written whole and in the order it should be drawn. Adding, renaming and
|
|
145
|
+
// reordering are all this one call.
|
|
146
|
+
configureBoard(input: ConfigureBoardInput): Promise<ConfigureBoardResult>;
|
|
147
|
+
// ⚠️ The five task operations answer with the ONE task that was written, never the whole board.
|
|
148
|
+
// A board holds up to five thousand tasks, and a one-card edit that answered with all of them
|
|
149
|
+
// would make every write pay for a full read — so the cache is patched from the answer here
|
|
150
|
+
// (`board-data.ts`) rather than refetched.
|
|
151
|
+
addBoardTask(input: AddBoardTaskInput): Promise<BoardTaskResult>;
|
|
152
|
+
updateBoardTask(input: UpdateBoardTaskInput): Promise<BoardTaskResult>;
|
|
153
|
+
// Where a task sits: its column, its parent, its place among its neighbours. ⚠️ A move names
|
|
154
|
+
// its NEIGHBOURS and never an index — the server mints the order key, so one drag writes one
|
|
155
|
+
// task and renumbers nothing.
|
|
156
|
+
moveBoardTask(input: MoveBoardTaskInput): Promise<BoardTaskResult>;
|
|
157
|
+
// ⚠️ Deleting cascades to every descendant and `deleted` counts them, so a surface can say what
|
|
158
|
+
// it is about to do before it does it rather than afterwards.
|
|
159
|
+
deleteBoardTask(input: DeleteBoardTaskInput): Promise<DeleteBoardTaskResult>;
|
|
160
|
+
|
|
130
161
|
// Walks the browser through the portal's OAuth flow without asking anybody anything: Gate is the
|
|
131
162
|
// identity provider Cloudflare Access consumes, so a signed-in person is already known there
|
|
132
163
|
// (#60). It navigates away — the caller renders no button for it and gets no answer back.
|
package/src/i18n/de.json
CHANGED
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"tree.kind.attachment": "Datei",
|
|
51
51
|
"tree.kind.table": "Tabelle",
|
|
52
52
|
"tree.kind.agent": "Agent",
|
|
53
|
+
"tree.kind.board": "Board",
|
|
53
54
|
"tree.kind.flow": "Flow",
|
|
54
55
|
"tree.move.action": "Verschieben nach …",
|
|
55
56
|
"tree.move.title": "{title} verschieben",
|
|
@@ -108,6 +109,7 @@
|
|
|
108
109
|
"node.kind.attachment": "Upload",
|
|
109
110
|
"node.kind.table": "Tabelle",
|
|
110
111
|
"node.kind.agent": "Agent",
|
|
112
|
+
"node.kind.board": "Board",
|
|
111
113
|
"node.kind.flow": "Flow",
|
|
112
114
|
"node.share": "Freigeben",
|
|
113
115
|
"node.shareAction": "Zugriff geben",
|
|
@@ -422,5 +424,90 @@
|
|
|
422
424
|
"common.loading": "Wird geladen …",
|
|
423
425
|
"common.retry": "Erneut versuchen",
|
|
424
426
|
"common.close": "Schließen",
|
|
425
|
-
"common.unavailable": "Intel ist derzeit nicht verfügbar."
|
|
427
|
+
"common.unavailable": "Intel ist derzeit nicht verfügbar.",
|
|
428
|
+
"board.summary": "{tasks} Aufgaben in {statuses} Status",
|
|
429
|
+
"board.views": "Ansichten dieses Boards",
|
|
430
|
+
"board.view.kanban": "Board",
|
|
431
|
+
"board.view.table": "Tabelle",
|
|
432
|
+
"board.view.calendar": "Kalender",
|
|
433
|
+
"board.view.graph": "Graph",
|
|
434
|
+
"board.view.gantt": "Gantt",
|
|
435
|
+
"board.statuses": "Status",
|
|
436
|
+
"board.statusLabel": "Statusname",
|
|
437
|
+
"board.statusUp": "Status nach oben",
|
|
438
|
+
"board.statusDown": "Status nach unten",
|
|
439
|
+
"board.statusRemove": "Status {label} entfernen",
|
|
440
|
+
"board.addStatus": "Neuer Status",
|
|
441
|
+
"board.shelfFixed": "Immer der letzte",
|
|
442
|
+
"board.showArchived": "Archivierte zeigen",
|
|
443
|
+
"board.addTask": "Neue Aufgabe",
|
|
444
|
+
"board.empty": "Auf diesem Board steht noch nichts. Lege oben die erste Aufgabe an.",
|
|
445
|
+
"board.noTasks": "Hier passt nichts.",
|
|
446
|
+
"board.laneEmpty": "Leer",
|
|
447
|
+
"board.archivedEmpty": "Nichts archiviert.",
|
|
448
|
+
"board.blocked": "Blockiert",
|
|
449
|
+
"board.open": "Offen",
|
|
450
|
+
"board.subtaskCount": "{done}/{total} Teilaufgaben",
|
|
451
|
+
"board.unassigned": "Nicht zugewiesen",
|
|
452
|
+
"board.assignee.me": "Ich",
|
|
453
|
+
"board.assignee.unnamed": "Zugewiesen",
|
|
454
|
+
"board.unknownTask": "Aufgabe für dich nicht verfügbar",
|
|
455
|
+
"board.column.title": "Titel",
|
|
456
|
+
"board.column.status": "Status",
|
|
457
|
+
"board.column.assignee": "Zuständig",
|
|
458
|
+
"board.column.startDate": "Startdatum",
|
|
459
|
+
"board.column.dueDate": "Fällig am",
|
|
460
|
+
"board.column.labels": "Labels",
|
|
461
|
+
"board.columns": "Spalten",
|
|
462
|
+
"board.filterTitle": "Nach Titel filtern",
|
|
463
|
+
"board.groupBy": "Gruppieren nach",
|
|
464
|
+
"board.groupBy.none": "Nichts",
|
|
465
|
+
"board.groupBy.status": "Status",
|
|
466
|
+
"board.groupBy.assignee": "Zuständig",
|
|
467
|
+
"board.toggleSubtasks": "Teilaufgaben von {task} ein- oder ausblenden",
|
|
468
|
+
"board.calendar.previous": "Vorheriger Monat",
|
|
469
|
+
"board.calendar.next": "Nächster Monat",
|
|
470
|
+
"board.calendar.today": "Heute",
|
|
471
|
+
"board.calendar.more": "+{count} weitere",
|
|
472
|
+
"board.calendar.undated": "{count} ohne Fälligkeitsdatum — nicht im Raster, aber auf dem Board und in der Tabelle.",
|
|
473
|
+
"board.gantt.scale": "Maßstab",
|
|
474
|
+
"board.gantt.scale.days": "Tage",
|
|
475
|
+
"board.gantt.scale.weeks": "Wochen",
|
|
476
|
+
"board.gantt.scale.months": "Monate",
|
|
477
|
+
"board.gantt.today": "Heute",
|
|
478
|
+
"board.gantt.move": "{task} verschieben",
|
|
479
|
+
"board.gantt.resizeStart": "Beginn von {task} ändern",
|
|
480
|
+
"board.gantt.resizeEnd": "Fälligkeit von {task} ändern",
|
|
481
|
+
"board.gantt.spanLabel": "{start} bis {end}",
|
|
482
|
+
"board.gantt.waitsFor": "wartet auf {tasks}",
|
|
483
|
+
"board.gantt.hiddenLinks": "{count} Abhängigkeiten sind nicht gezeichnet: die erwartete Aufgabe hat hier keinen Balken.",
|
|
484
|
+
"board.gantt.undated": "Ohne Datum",
|
|
485
|
+
"board.gantt.undatedCount": "{count} ohne Datum — auf dem Board, aber auf keiner Zeitachse.",
|
|
486
|
+
"board.gantt.nothingDated": "Nichts auf diesem Board hat ein Datum. Gib einer Aufgabe ein Fälligkeitsdatum, dann erscheint sie hier.",
|
|
487
|
+
"board.graph.subtree": "Teilbaum",
|
|
488
|
+
"board.graph.wholeBoard": "Ganzes Board",
|
|
489
|
+
"board.graph.parentEdge": "enthält",
|
|
490
|
+
"board.graph.dependsEdge": "wartet auf",
|
|
491
|
+
"board.detail": "Aufgabe",
|
|
492
|
+
"board.description": "Beschreibung",
|
|
493
|
+
"board.subtasks": "Teilaufgaben",
|
|
494
|
+
"board.addSubtask": "Neue Teilaufgabe",
|
|
495
|
+
"board.dependencies": "Wartet auf",
|
|
496
|
+
"board.addDependency": "Abhängigkeit hinzufügen",
|
|
497
|
+
"board.removeDependency": "Diese Abhängigkeit entfernen",
|
|
498
|
+
"board.references": "Referenzen",
|
|
499
|
+
"board.addReference": "Referenz hinzufügen",
|
|
500
|
+
"board.removeReference": "Diese Referenz entfernen",
|
|
501
|
+
"board.addLabel": "Neues Label",
|
|
502
|
+
"board.removeLabel": "Label {label} entfernen",
|
|
503
|
+
"board.deleteTask": "Aufgabe löschen",
|
|
504
|
+
"board.deleteConfirm": "„{task}“ zu löschen entfernt die Aufgabe und alles darunter: insgesamt {count}. Abhängigkeiten darauf werden entfernt.",
|
|
505
|
+
"board.deleted": "Gelöscht: {count}.",
|
|
506
|
+
"board.archivedHint": "Diese Aufgabe liegt im Archiv. Ein Wechsel in einen anderen Status holt sie zurück.",
|
|
507
|
+
"board.dnd.start": "{task} aufgenommen",
|
|
508
|
+
"board.dnd.over": "{task} über {lane}",
|
|
509
|
+
"board.dnd.end": "{task} in {lane} abgelegt",
|
|
510
|
+
"board.dnd.cancel": "Ablegen von {task} abgebrochen",
|
|
511
|
+
"board.dragCard": "{task} ziehen",
|
|
512
|
+
"board.statusTerminal": "Gilt als erledigt"
|
|
426
513
|
}
|
package/src/i18n/en.json
CHANGED
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"tree.kind.attachment": "File",
|
|
51
51
|
"tree.kind.table": "Table",
|
|
52
52
|
"tree.kind.agent": "Agent",
|
|
53
|
+
"tree.kind.board": "Board",
|
|
53
54
|
"tree.kind.flow": "Flow",
|
|
54
55
|
"tree.move.action": "Move to…",
|
|
55
56
|
"tree.move.title": "Move {title}",
|
|
@@ -108,6 +109,7 @@
|
|
|
108
109
|
"node.kind.attachment": "Upload",
|
|
109
110
|
"node.kind.table": "Table",
|
|
110
111
|
"node.kind.agent": "Agent",
|
|
112
|
+
"node.kind.board": "Board",
|
|
111
113
|
"node.kind.flow": "Flow",
|
|
112
114
|
"node.share": "Share",
|
|
113
115
|
"node.shareAction": "Grant access",
|
|
@@ -422,5 +424,90 @@
|
|
|
422
424
|
"common.loading": "Loading…",
|
|
423
425
|
"common.retry": "Try again",
|
|
424
426
|
"common.close": "Close",
|
|
425
|
-
"common.unavailable": "Intel is currently unavailable."
|
|
427
|
+
"common.unavailable": "Intel is currently unavailable.",
|
|
428
|
+
"board.summary": "{tasks} tasks in {statuses} statuses",
|
|
429
|
+
"board.views": "Views of this board",
|
|
430
|
+
"board.view.kanban": "Board",
|
|
431
|
+
"board.view.table": "Table",
|
|
432
|
+
"board.view.calendar": "Calendar",
|
|
433
|
+
"board.view.graph": "Graph",
|
|
434
|
+
"board.view.gantt": "Gantt",
|
|
435
|
+
"board.statuses": "Statuses",
|
|
436
|
+
"board.statusLabel": "Status name",
|
|
437
|
+
"board.statusUp": "Move status up",
|
|
438
|
+
"board.statusDown": "Move status down",
|
|
439
|
+
"board.statusRemove": "Remove the status {label}",
|
|
440
|
+
"board.addStatus": "New status",
|
|
441
|
+
"board.shelfFixed": "Always the last one",
|
|
442
|
+
"board.showArchived": "Show archived",
|
|
443
|
+
"board.addTask": "New task",
|
|
444
|
+
"board.empty": "Nothing on this board yet. Add the first task above.",
|
|
445
|
+
"board.noTasks": "Nothing here that matches.",
|
|
446
|
+
"board.laneEmpty": "Empty",
|
|
447
|
+
"board.archivedEmpty": "Nothing archived.",
|
|
448
|
+
"board.blocked": "Blocked",
|
|
449
|
+
"board.open": "Open",
|
|
450
|
+
"board.subtaskCount": "{done}/{total} subtasks",
|
|
451
|
+
"board.unassigned": "Unassigned",
|
|
452
|
+
"board.assignee.me": "Me",
|
|
453
|
+
"board.assignee.unnamed": "Assigned",
|
|
454
|
+
"board.unknownTask": "Task not available to you",
|
|
455
|
+
"board.column.title": "Title",
|
|
456
|
+
"board.column.status": "Status",
|
|
457
|
+
"board.column.assignee": "Assignee",
|
|
458
|
+
"board.column.startDate": "Start date",
|
|
459
|
+
"board.column.dueDate": "Due date",
|
|
460
|
+
"board.column.labels": "Labels",
|
|
461
|
+
"board.columns": "Columns",
|
|
462
|
+
"board.filterTitle": "Filter by title",
|
|
463
|
+
"board.groupBy": "Group by",
|
|
464
|
+
"board.groupBy.none": "Nothing",
|
|
465
|
+
"board.groupBy.status": "Status",
|
|
466
|
+
"board.groupBy.assignee": "Assignee",
|
|
467
|
+
"board.toggleSubtasks": "Show or hide the subtasks of {task}",
|
|
468
|
+
"board.calendar.previous": "Previous month",
|
|
469
|
+
"board.calendar.next": "Next month",
|
|
470
|
+
"board.calendar.today": "Today",
|
|
471
|
+
"board.calendar.more": "+{count} more",
|
|
472
|
+
"board.calendar.undated": "{count} without a due date — not in this grid, but on the board and in the table.",
|
|
473
|
+
"board.gantt.scale": "Scale",
|
|
474
|
+
"board.gantt.scale.days": "Days",
|
|
475
|
+
"board.gantt.scale.weeks": "Weeks",
|
|
476
|
+
"board.gantt.scale.months": "Months",
|
|
477
|
+
"board.gantt.today": "Today",
|
|
478
|
+
"board.gantt.move": "Move {task}",
|
|
479
|
+
"board.gantt.resizeStart": "Change when {task} starts",
|
|
480
|
+
"board.gantt.resizeEnd": "Change when {task} is due",
|
|
481
|
+
"board.gantt.spanLabel": "{start} to {end}",
|
|
482
|
+
"board.gantt.waitsFor": "waits for {tasks}",
|
|
483
|
+
"board.gantt.hiddenLinks": "{count} dependencies are not drawn: the task waited for has no bar here.",
|
|
484
|
+
"board.gantt.undated": "Without dates",
|
|
485
|
+
"board.gantt.undatedCount": "{count} without dates — on the board, but on no timeline.",
|
|
486
|
+
"board.gantt.nothingDated": "Nothing on this board has a date yet. Give a task a due date and it appears here.",
|
|
487
|
+
"board.graph.subtree": "Subtree",
|
|
488
|
+
"board.graph.wholeBoard": "Whole board",
|
|
489
|
+
"board.graph.parentEdge": "contains",
|
|
490
|
+
"board.graph.dependsEdge": "waits for",
|
|
491
|
+
"board.detail": "Task",
|
|
492
|
+
"board.description": "Description",
|
|
493
|
+
"board.subtasks": "Subtasks",
|
|
494
|
+
"board.addSubtask": "New subtask",
|
|
495
|
+
"board.dependencies": "Waits for",
|
|
496
|
+
"board.addDependency": "Add a dependency",
|
|
497
|
+
"board.removeDependency": "Remove this dependency",
|
|
498
|
+
"board.references": "References",
|
|
499
|
+
"board.addReference": "Add a reference",
|
|
500
|
+
"board.removeReference": "Remove this reference",
|
|
501
|
+
"board.addLabel": "New label",
|
|
502
|
+
"board.removeLabel": "Remove the label {label}",
|
|
503
|
+
"board.deleteTask": "Delete task",
|
|
504
|
+
"board.deleteConfirm": "Deleting “{task}” removes it and everything below it: {count} in total. Any dependency pointing into that is cleared.",
|
|
505
|
+
"board.deleted": "Deleted: {count}.",
|
|
506
|
+
"board.archivedHint": "This task is on the archive shelf. Move it to another status to bring it back.",
|
|
507
|
+
"board.dnd.start": "Picked up {task}",
|
|
508
|
+
"board.dnd.over": "{task} is over {lane}",
|
|
509
|
+
"board.dnd.end": "Dropped {task} into {lane}",
|
|
510
|
+
"board.dnd.cancel": "Dropping {task} was cancelled",
|
|
511
|
+
"board.dragCard": "Drag {task}",
|
|
512
|
+
"board.statusTerminal": "Means done"
|
|
426
513
|
}
|