@asteby/metacore-runtime-react 23.7.0 → 23.8.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 +21 -0
- package/dist/action-modal-dispatcher.js +14 -6
- package/dist/custom-stages.d.ts +13 -1
- package/dist/custom-stages.d.ts.map +1 -1
- package/dist/custom-stages.js +3 -3
- package/dist/dynamic-kanban.d.ts.map +1 -1
- package/dist/dynamic-kanban.js +259 -43
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/model-action-toolbar.d.ts.map +1 -1
- package/dist/model-action-toolbar.js +3 -1
- package/dist/stage-layout.d.ts +23 -0
- package/dist/stage-layout.d.ts.map +1 -0
- package/dist/stage-layout.js +70 -0
- package/package.json +1 -1
- package/src/__tests__/dynamic-kanban-lane-reorder.test.tsx +228 -0
- package/src/__tests__/model-action-toolbar-i18n.test.tsx +101 -0
- package/src/__tests__/stage-layout.test.tsx +210 -0
- package/src/action-modal-dispatcher.tsx +16 -8
- package/src/custom-stages.tsx +30 -3
- package/src/dynamic-kanban.tsx +474 -144
- package/src/index.ts +5 -0
- package/src/model-action-toolbar.tsx +9 -1
- package/src/stage-layout.ts +93 -0
package/src/index.ts
CHANGED
|
@@ -54,6 +54,11 @@ export {
|
|
|
54
54
|
type CustomStageFilterOp,
|
|
55
55
|
type UseCustomStagesResult,
|
|
56
56
|
} from './custom-stages'
|
|
57
|
+
export {
|
|
58
|
+
useStageLayout,
|
|
59
|
+
type StageLayout,
|
|
60
|
+
type UseStageLayoutResult,
|
|
61
|
+
} from './stage-layout'
|
|
57
62
|
export {
|
|
58
63
|
DynamicView,
|
|
59
64
|
resolveViewRenderer,
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
// it internally, and bespoke host pages (e.g. ops `/m/$model`) mount it directly
|
|
19
19
|
// next to their own toolbar. Hosts never reimplement action-button plumbing.
|
|
20
20
|
import { useEffect, useMemo, useState } from 'react'
|
|
21
|
+
import { useTranslation } from 'react-i18next'
|
|
21
22
|
import { Button } from '@asteby/metacore-ui/primitives'
|
|
22
23
|
import { useApi } from './api-context'
|
|
23
24
|
import { useMetadataCache } from './metadata-cache'
|
|
@@ -109,6 +110,7 @@ export function ModelActionToolbar({
|
|
|
109
110
|
onChange,
|
|
110
111
|
className,
|
|
111
112
|
}: ModelActionToolbarProps) {
|
|
113
|
+
const { t } = useTranslation()
|
|
112
114
|
const all = useModelActions(model, placements, actions)
|
|
113
115
|
// Capability gating — always-true without a <PermissionsProvider>. Custom
|
|
114
116
|
// table/create actions map onto `lowercase(model).<action_key>`.
|
|
@@ -135,7 +137,13 @@ export function ModelActionToolbar({
|
|
|
135
137
|
style={a.color && !isCreate ? { borderColor: a.color, color: a.color } : undefined}
|
|
136
138
|
>
|
|
137
139
|
<DynamicIcon name={a.icon || (isCreate ? 'Plus' : 'Zap')} className="mr-2 h-4 w-4" />
|
|
138
|
-
{a.label
|
|
140
|
+
{/* `a.label` is an addon-contributed i18n key (e.g.
|
|
141
|
+
"integration_github.action.create_issue.label"); the addon's
|
|
142
|
+
locale bundle loads asynchronously, so translate at render — a
|
|
143
|
+
bare `{a.label}` prints the raw key until (and after) the bundle
|
|
144
|
+
lands because nothing re-derives it. defaultValue keeps an
|
|
145
|
+
already-localized label untouched. */}
|
|
146
|
+
{t(a.label, { defaultValue: a.label })}
|
|
139
147
|
</Button>
|
|
140
148
|
)
|
|
141
149
|
})}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Stage layout — per-org persistence of the DynamicKanban lane order. Lets a
|
|
2
|
+
// user drag the board's columns (declared stages, custom stages and smart
|
|
3
|
+
// lanes alike) into any order; the chosen order is saved server-side and
|
|
4
|
+
// re-applied on load (the ops backend stamps it onto `metadata.stages[].order`
|
|
5
|
+
// / `smart_lanes[].order`, so the board already paints ordered — this hook only
|
|
6
|
+
// owns the drag + the optimistic PUT).
|
|
7
|
+
//
|
|
8
|
+
// Non-intrusive by design: mirrors `useStageAutomations`. If the host wires no
|
|
9
|
+
// `/stage-layout` endpoint, the GET 404s, `available` stays false, and the lane
|
|
10
|
+
// drag simply never turns on — the kanban keeps working untouched.
|
|
11
|
+
//
|
|
12
|
+
// Contract (matches the ops backend; envelope is {success, data} → read .data):
|
|
13
|
+
// GET /stage-layout?model=<m> → { model, stage_order: string[] } | null
|
|
14
|
+
// PUT /stage-layout { model, stage_order: string[] } (full order)
|
|
15
|
+
// DELETE /stage-layout?model=<m> → reset to the declared order
|
|
16
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
17
|
+
import { useApi } from './api-context'
|
|
18
|
+
|
|
19
|
+
export interface StageLayout {
|
|
20
|
+
model: string
|
|
21
|
+
/** The full lane order — every lane key (stages + smart lanes) in order. */
|
|
22
|
+
stage_order: string[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface UseStageLayoutResult {
|
|
26
|
+
/** True only after a successful GET — gates the lane drag + reset affordance. */
|
|
27
|
+
available: boolean
|
|
28
|
+
/** True when a custom order is stored server-side (drives the reset affordance). */
|
|
29
|
+
hasCustomLayout: boolean
|
|
30
|
+
/** Persist the full lane order (the new order of every lane key). Throws on failure. */
|
|
31
|
+
save: (order: string[]) => Promise<void>
|
|
32
|
+
/** Drop the stored order, reverting to the declared stage order. */
|
|
33
|
+
reset: () => Promise<void>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function unwrap(res: { data: any }): any {
|
|
37
|
+
const body = res?.data
|
|
38
|
+
if (body && typeof body === 'object' && 'data' in body) return body.data
|
|
39
|
+
return body
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Loads a model's saved lane order (only to learn availability + whether a
|
|
44
|
+
* custom order exists) and exposes save/reset. A missing endpoint degrades to
|
|
45
|
+
* `available: false` so the board's lane drag stays off; a real save failure
|
|
46
|
+
* re-throws so the caller can revert its optimistic reorder.
|
|
47
|
+
*/
|
|
48
|
+
export function useStageLayout(model: string): UseStageLayoutResult {
|
|
49
|
+
const api = useApi()
|
|
50
|
+
const [available, setAvailable] = useState(false)
|
|
51
|
+
const [hasCustomLayout, setHasCustomLayout] = useState(false)
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
let cancelled = false
|
|
55
|
+
api
|
|
56
|
+
.get(`/stage-layout?model=${encodeURIComponent(model)}`)
|
|
57
|
+
.then((res) => {
|
|
58
|
+
if (cancelled) return
|
|
59
|
+
setAvailable(true)
|
|
60
|
+
const data = unwrap(res)
|
|
61
|
+
const order = data?.stage_order
|
|
62
|
+
setHasCustomLayout(Array.isArray(order) && order.length > 0)
|
|
63
|
+
})
|
|
64
|
+
.catch(() => {
|
|
65
|
+
// Endpoint absent or errored — leave lane drag off.
|
|
66
|
+
if (!cancelled) setAvailable(false)
|
|
67
|
+
})
|
|
68
|
+
return () => {
|
|
69
|
+
cancelled = true
|
|
70
|
+
}
|
|
71
|
+
}, [api, model])
|
|
72
|
+
|
|
73
|
+
const save = useCallback(
|
|
74
|
+
async (order: string[]) => {
|
|
75
|
+
const res = (await api.put('/stage-layout', {
|
|
76
|
+
model,
|
|
77
|
+
stage_order: order,
|
|
78
|
+
})) as { data?: { success?: boolean; message?: string } }
|
|
79
|
+
if (res?.data && res.data.success === false) {
|
|
80
|
+
throw new Error(res.data.message || 'stage_layout_save_failed')
|
|
81
|
+
}
|
|
82
|
+
setHasCustomLayout(true)
|
|
83
|
+
},
|
|
84
|
+
[api, model],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
const reset = useCallback(async () => {
|
|
88
|
+
await api.delete(`/stage-layout?model=${encodeURIComponent(model)}`)
|
|
89
|
+
setHasCustomLayout(false)
|
|
90
|
+
}, [api, model])
|
|
91
|
+
|
|
92
|
+
return { available, hasCustomLayout, save, reset }
|
|
93
|
+
}
|