mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
fix: follow a saved page item in the tab's own workspace and restore in-frame rows
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
09ab9b8e6a
commit
6fb48c64df
@@ -473,6 +473,12 @@
|
||||
current.path = npath
|
||||
}
|
||||
|
||||
/** The path the resource has in `ws`: after a save, the one it was saved under. Each
|
||||
* workspace-specific version keeps its own, so the selected one says nothing about `ws`. */
|
||||
export function pathIn(ws: string): string | undefined {
|
||||
return initialStates[ws]?.path
|
||||
}
|
||||
|
||||
/** Whether the write landed. It toasts its own failure, so most callers ignore this;
|
||||
* one that follows the save with bookkeeping of its own has to know not to. */
|
||||
export async function save(): Promise<boolean> {
|
||||
|
||||
@@ -41,8 +41,9 @@
|
||||
inline?: boolean
|
||||
onRestored?: () => void
|
||||
/** Fires after Save has written, for a caller showing state derived from the
|
||||
* resource — `onRestored` only covers restoring an old version. */
|
||||
onSaved?: () => void
|
||||
* resource — `onRestored` only covers restoring an old version. `path` is where the
|
||||
* resource now lives in this drawer's workspace, undefined when the save failed. */
|
||||
onSaved?: (path: string | undefined) => void
|
||||
} = $props()
|
||||
|
||||
let drawer: Drawer | undefined = $state()
|
||||
@@ -53,7 +54,8 @@
|
||||
|
||||
let resourceEditor:
|
||||
| {
|
||||
save: () => void
|
||||
save: () => Promise<boolean>
|
||||
pathIn: (ws: string) => string | undefined
|
||||
localDraftDeployed: () => unknown
|
||||
localDraftCurrent: () => unknown
|
||||
discardLocalDraft: () => void
|
||||
@@ -223,10 +225,10 @@
|
||||
// Closed before the write is awaited, the way it always was: `save()` toasts its
|
||||
// own failures and never rejects, so waiting would only add visible lag to every
|
||||
// caller of this drawer. `onSaved` still fires after the write lands.
|
||||
const saved = resourceEditor?.save()
|
||||
const editor = resourceEditor
|
||||
const saved = editor?.save()
|
||||
drawer?.closeDrawer()
|
||||
await saved
|
||||
onSaved?.()
|
||||
onSaved?.((await saved) ? editor?.pathIn(effectiveWorkspace) : undefined)
|
||||
}}
|
||||
disabled={!canSave}
|
||||
>
|
||||
|
||||
@@ -50,7 +50,8 @@
|
||||
/** Render in place, filling the parent, with no drawer or close button — for a host
|
||||
* that gives the editor a whole pane. */
|
||||
inline?: boolean
|
||||
/** Fires once a save lands, with the path the variable now lives at. */
|
||||
/** Fires once a save lands, with the path the variable now lives at in `workspace` —
|
||||
* not in the workspace-specific version selected, which can be another's. */
|
||||
onSaved?: (path: string) => void
|
||||
} = $props()
|
||||
// Sole ambient read in this file: the acting workspace is an input, and only its
|
||||
@@ -271,7 +272,7 @@
|
||||
|
||||
async function save(): Promise<void> {
|
||||
const dirty = dirtyWorkspaces
|
||||
const savedPath = current?.path ?? editPath ?? ''
|
||||
const savedPath = (curWs ? states[curWs]?.draft?.path : undefined) ?? editPath ?? ''
|
||||
try {
|
||||
for (const ws of dirty) {
|
||||
const s = states[ws].draft!
|
||||
|
||||
@@ -144,7 +144,9 @@
|
||||
bind:this={resourceEditor}
|
||||
inline
|
||||
workspace={workspaceId}
|
||||
on:refresh={(e) => onSaved(typeof e.detail === 'string' ? e.detail : undefined)}
|
||||
onSaved={(path) => {
|
||||
if (path !== undefined) onSaved(path)
|
||||
}}
|
||||
onRestored={() => savedNonce++}
|
||||
/>
|
||||
{:else if triggerKey}
|
||||
|
||||
@@ -210,11 +210,11 @@ export function hydratePreviewTabs(session: {
|
||||
seen.add(t.id)
|
||||
// Rebuilt field-by-field so stray properties on old saved records (e.g. the
|
||||
// retired `pinned` flag) don't survive hydration and get persisted back.
|
||||
// A list page saved with a row's drawer open comes back as that row's own tab.
|
||||
const url = pageItemLocation(t.url)
|
||||
// A list page saved with a row's drawer open comes back as that row's own tab, whether the
|
||||
// row was asked for (`url`) or opened inside the frame (only its observed `loc` says so).
|
||||
const loc = t.loc || t.url
|
||||
const stale = parsePageItemRoute(url) || pageItemLocation(loc) !== loc
|
||||
tabs.push({ id: t.id, url, loc: stale ? url : loc })
|
||||
const item = [t.url, loc].map(pageItemLocation).find((u) => parsePageItemRoute(u))
|
||||
tabs.push(item ? { id: t.id, url: item, loc: item } : { id: t.id, url: t.url, loc })
|
||||
}
|
||||
if (tabs.length > 0) {
|
||||
const wantActive = session.activePreviewTabId
|
||||
|
||||
@@ -245,13 +245,13 @@ describe('page item tabs', () => {
|
||||
const snap = hydratePreviewTabs({
|
||||
previewTabs: [
|
||||
{ id: 'a', url: '/schedules#u/me/daily', loc: '/schedules?path=u#u/me/daily' },
|
||||
// A drawer opened inside the frame, with the command still on the list.
|
||||
// A drawer opened inside the frame: the command is still the bare list.
|
||||
{ id: 'b', url: '/variables', loc: '/variables#u/me/token' }
|
||||
]
|
||||
})
|
||||
expect(snap.tabs).toEqual([
|
||||
{ id: 'a', url: 'pageitem:schedule/u%2Fme%2Fdaily', loc: 'pageitem:schedule/u%2Fme%2Fdaily' },
|
||||
{ id: 'b', url: '/variables', loc: '/variables' }
|
||||
{ id: 'b', url: 'pageitem:variable/u%2Fme%2Ftoken', loc: 'pageitem:variable/u%2Fme%2Ftoken' }
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user