@panneau/actions 4.1.1 → 5.0.0-alpha.2
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/README.md +75 -0
- package/dist/ActionRunner.css +84 -0
- package/dist/ActionRunner.module.js +34 -0
- package/dist/ActionRunner.module.js.map +1 -0
- package/dist/ActionRunnerProvider.d.ts +36 -0
- package/dist/ActionRunnerProvider.d.ts.map +1 -0
- package/dist/ActionRunnerProvider.js +245 -0
- package/dist/ActionRunnerProvider.js.map +1 -0
- package/dist/ActionRuns.d.ts +12 -0
- package/dist/ActionRuns.d.ts.map +1 -0
- package/dist/ActionRuns.js +128 -0
- package/dist/ActionRuns.js.map +1 -0
- package/dist/ParametersDialog.d.ts +19 -0
- package/dist/ParametersDialog.d.ts.map +1 -0
- package/dist/ParametersDialog.js +119 -0
- package/dist/ParametersDialog.js.map +1 -0
- package/dist/ResultsDialog.d.ts +10 -0
- package/dist/ResultsDialog.d.ts.map +1 -0
- package/dist/ResultsDialog.js +130 -0
- package/dist/ResultsDialog.js.map +1 -0
- package/dist/context.d.ts +10 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/context.js.map +1 -0
- package/dist/describe.d.ts +8 -0
- package/dist/describe.d.ts.map +1 -0
- package/dist/describe.js +32 -0
- package/dist/describe.js.map +1 -0
- package/dist/index.d.ts +9 -10
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -74
- package/dist/messages.d.ts +2 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +1 -0
- package/dist/request.d.ts +15 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +50 -0
- package/dist/request.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/useActionRunner.d.ts +21 -0
- package/dist/useActionRunner.d.ts.map +1 -0
- package/dist/useActionRunner.js +34 -0
- package/dist/useActionRunner.js.map +1 -0
- package/package.json +29 -50
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# @panneau/actions
|
|
2
|
+
|
|
3
|
+
Runs the actions of lists and forms: parameters in a dialog, the action through a data adapter, its result, and queued runs. Why it is its own package: [decision 0023](../../docs/decisions/0023-action-runner.md).
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
7
|
+
|
|
8
|
+
import { ActionRunnerProvider, ActionRuns, useActionRunner } from '@panneau/actions';
|
|
9
|
+
import { createRestAdapter } from '@panneau/data';
|
|
10
|
+
import { invalidateActionResult } from '@panneau/data/query';
|
|
11
|
+
import { ListView } from '@panneau/lists';
|
|
12
|
+
import { PanneauRoot } from '@panneau/ui';
|
|
13
|
+
|
|
14
|
+
const adapter = createRestAdapter({ baseUrl: '/panneau/api' });
|
|
15
|
+
|
|
16
|
+
function App() {
|
|
17
|
+
const client = useQueryClient();
|
|
18
|
+
return (
|
|
19
|
+
<PanneauRoot>
|
|
20
|
+
<ActionRunnerProvider
|
|
21
|
+
adapter={adapter}
|
|
22
|
+
onResult={(result, { resource, target }) =>
|
|
23
|
+
invalidateActionResult(client, resource, result, target.selection)
|
|
24
|
+
}
|
|
25
|
+
onNavigate={(to) => navigate(typeof to === 'string' ? to : routeUrl(to))}
|
|
26
|
+
>
|
|
27
|
+
<Pages />
|
|
28
|
+
<ActionRuns />
|
|
29
|
+
</ActionRunnerProvider>
|
|
30
|
+
</PanneauRoot>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function Pages() {
|
|
35
|
+
const runAction = useActionRunner('pages');
|
|
36
|
+
return (
|
|
37
|
+
<ListView
|
|
38
|
+
list={list}
|
|
39
|
+
items={items}
|
|
40
|
+
columns={columns}
|
|
41
|
+
actions={actions}
|
|
42
|
+
onAction={runAction}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Export | Content |
|
|
49
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
50
|
+
| `@panneau/actions` | `ActionRunnerProvider`, `useActionRunner()`, `ActionRuns` and `useActionRuns()`, the dialogs (`ParametersDialog`, `ResultsDialog`), and their helpers |
|
|
51
|
+
|
|
52
|
+
## What the runner does
|
|
53
|
+
|
|
54
|
+
`useActionRunner(resource)` returns a function that takes a list's `ListActionEvent` (or `{ action, scope: 'item', item }` for the item of a form). Lists confirm the action first, when it asks for it; then the runner:
|
|
55
|
+
|
|
56
|
+
- **`server` actions:**
|
|
57
|
+
1. asks for the parameters (`fields`, in their `layout`) in a dialog, with the forms of `@panneau/forms`: client validation, then the `422` errors of the action endpoint on the fields;
|
|
58
|
+
2. runs the action through the adapter, with the item or the selection (`ids`, or the list query with exceptions), or nothing for the resource;
|
|
59
|
+
3. shows the result in a toast: its `message`, the counts of what didn't succeed, and a "Details" button that lists those items with their message or reason;
|
|
60
|
+
4. calls `onResult` (refresh what it changed), opens the `download`, and goes to the `redirect`.
|
|
61
|
+
- **Queued runs** (`202`): a toast says that the action runs in the background, the runner checks the run every `pollInterval` until it ends, then shows its result as above. `ActionRuns` lists the runs, with their progress, a button to cancel them (when the adapter has `cancelActionRun()`), and one to dismiss them once they have ended.
|
|
62
|
+
- **`link` actions** go to their `url` (with `{id}`) or `route`, through `onNavigate`.
|
|
63
|
+
- **`client` actions** render the handler that their `component` names: one of `clientActions`, or of the plugins of `PanneauProvider` (`definePlugin({ actions: { 'share-dialog': ShareDialog } })`). It gets `ClientActionProps`, and ends with `onDone(result?)` or `onCancel()`.
|
|
64
|
+
|
|
65
|
+
The function resolves with what happened (`{ status: 'done', result }`, `{ status: 'queued', run }` or `{ status: 'navigated', to }`), with `false` when the user cancelled the parameters (a list then keeps its selection), and rejects when the action failed (a list then shows it in a toast).
|
|
66
|
+
|
|
67
|
+
## The adapter
|
|
68
|
+
|
|
69
|
+
The runner only needs the action methods of a `DataAdapter`: `runAction()`, `getActionRun()` and, to cancel runs, `cancelActionRun()`. `createRestAdapter()` of `@panneau/data` has them. A product with its own API implements those three, and errors with a `problem` for validation errors (`422`). The runner doesn't need TanStack Query.
|
|
70
|
+
|
|
71
|
+
## Where to put it
|
|
72
|
+
|
|
73
|
+
Inside `PanneauRoot` (dialogs and toasts render there), and above the router of an app, so that it follows runs from page to page. It only knows the runs that it started.
|
|
74
|
+
|
|
75
|
+
The package layout, scripts and API reports are explained in [`docs/packages.md`](../../docs/packages.md).
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
.panneau-action-runner-parameters {
|
|
2
|
+
margin: 0;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.panneau-action-runner-summary {
|
|
6
|
+
margin: 0 0 var(--panneau-space-3);
|
|
7
|
+
color: var(--panneau-color-text-muted);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.panneau-action-runner-results {
|
|
11
|
+
display: grid;
|
|
12
|
+
gap: var(--panneau-space-2);
|
|
13
|
+
margin: 0;
|
|
14
|
+
padding: 0;
|
|
15
|
+
list-style: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.panneau-action-runner-result {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-wrap: wrap;
|
|
21
|
+
align-items: center;
|
|
22
|
+
gap: var(--panneau-space-2);
|
|
23
|
+
padding: var(--panneau-space-2) 0;
|
|
24
|
+
border-bottom: var(--panneau-border-width) solid var(--panneau-color-border);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.panneau-action-runner-result-item {
|
|
28
|
+
font-weight: var(--panneau-font-weight-medium);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.panneau-action-runner-result-reason {
|
|
32
|
+
flex: 1 1 12rem;
|
|
33
|
+
color: var(--panneau-color-text-muted);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.panneau-action-runner-runs {
|
|
37
|
+
display: grid;
|
|
38
|
+
gap: var(--panneau-space-3);
|
|
39
|
+
margin: 0;
|
|
40
|
+
padding: 0;
|
|
41
|
+
list-style: none;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.panneau-action-runner-run {
|
|
45
|
+
display: grid;
|
|
46
|
+
grid-template-columns: 1fr auto;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: var(--panneau-space-1) var(--panneau-space-2);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.panneau-action-runner-run-label {
|
|
52
|
+
font-weight: var(--panneau-font-weight-medium);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.panneau-action-runner-run-status {
|
|
56
|
+
font-size: var(--panneau-font-size-sm);
|
|
57
|
+
color: var(--panneau-color-text-muted);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.panneau-action-runner-progress {
|
|
61
|
+
grid-column: 1 / -1;
|
|
62
|
+
display: grid;
|
|
63
|
+
gap: var(--panneau-space-1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.panneau-action-runner-track {
|
|
67
|
+
height: 0.375rem;
|
|
68
|
+
overflow: hidden;
|
|
69
|
+
background: var(--panneau-color-surface-sunken);
|
|
70
|
+
border-radius: var(--panneau-radius-full);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.panneau-action-runner-fill {
|
|
74
|
+
height: 100%;
|
|
75
|
+
background: var(--panneau-color-accent);
|
|
76
|
+
border-radius: inherit;
|
|
77
|
+
transition: width var(--panneau-duration) ease-out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@media (prefers-reduced-motion: reduce) {
|
|
81
|
+
.panneau-action-runner-fill {
|
|
82
|
+
transition: none;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import "./ActionRunner.css";
|
|
2
|
+
//#region src/ActionRunner.module.css
|
|
3
|
+
var parameters = "panneau-action-runner-parameters";
|
|
4
|
+
var summary = "panneau-action-runner-summary";
|
|
5
|
+
var results = "panneau-action-runner-results";
|
|
6
|
+
var result = "panneau-action-runner-result";
|
|
7
|
+
var resultItem = "panneau-action-runner-result-item";
|
|
8
|
+
var resultReason = "panneau-action-runner-result-reason";
|
|
9
|
+
var runs = "panneau-action-runner-runs";
|
|
10
|
+
var run = "panneau-action-runner-run";
|
|
11
|
+
var runLabel = "panneau-action-runner-run-label";
|
|
12
|
+
var runStatus = "panneau-action-runner-run-status";
|
|
13
|
+
var progress = "panneau-action-runner-progress";
|
|
14
|
+
var track = "panneau-action-runner-track";
|
|
15
|
+
var fill = "panneau-action-runner-fill";
|
|
16
|
+
var ActionRunner_module_default = {
|
|
17
|
+
parameters,
|
|
18
|
+
summary,
|
|
19
|
+
results,
|
|
20
|
+
result,
|
|
21
|
+
resultItem,
|
|
22
|
+
resultReason,
|
|
23
|
+
runs,
|
|
24
|
+
run,
|
|
25
|
+
runLabel,
|
|
26
|
+
runStatus,
|
|
27
|
+
progress,
|
|
28
|
+
track,
|
|
29
|
+
fill
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { ActionRunner_module_default as default, fill, parameters, progress, result, resultItem, resultReason, results, run, runLabel, runStatus, runs, summary, track };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=ActionRunner.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionRunner.module.js","names":[],"sources":["../src/ActionRunner.module.css"],"sourcesContent":[".parameters {\n margin: 0;\n}\n\n.summary {\n margin: 0 0 var(--panneau-space-3);\n color: var(--panneau-color-text-muted);\n}\n\n.results {\n display: grid;\n gap: var(--panneau-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.result {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--panneau-space-2);\n padding: var(--panneau-space-2) 0;\n border-bottom: var(--panneau-border-width) solid var(--panneau-color-border);\n}\n\n.resultItem {\n font-weight: var(--panneau-font-weight-medium);\n}\n\n.resultReason {\n flex: 1 1 12rem;\n color: var(--panneau-color-text-muted);\n}\n\n.runs {\n display: grid;\n gap: var(--panneau-space-3);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.run {\n display: grid;\n grid-template-columns: 1fr auto;\n align-items: center;\n gap: var(--panneau-space-1) var(--panneau-space-2);\n}\n\n.runLabel {\n font-weight: var(--panneau-font-weight-medium);\n}\n\n.runStatus {\n font-size: var(--panneau-font-size-sm);\n color: var(--panneau-color-text-muted);\n}\n\n.progress {\n grid-column: 1 / -1;\n display: grid;\n gap: var(--panneau-space-1);\n}\n\n.track {\n height: 0.375rem;\n overflow: hidden;\n background: var(--panneau-color-surface-sunken);\n border-radius: var(--panneau-radius-full);\n}\n\n.fill {\n height: 100%;\n background: var(--panneau-color-accent);\n border-radius: inherit;\n transition: width var(--panneau-duration) ease-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .fill {\n transition: none;\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { ActionResult, RouteReference } from '@panneau/protocol';
|
|
3
|
+
import type { ActionAdapter, ActionDownload, ActionResultContext, ClientActionHandler } from './types';
|
|
4
|
+
export interface ActionRunnerProviderProps {
|
|
5
|
+
/** Runs the actions: a `DataAdapter`, or any object with its action methods. */
|
|
6
|
+
adapter: ActionAdapter;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* Called with every result: of an action that finished at once, and of a queued run when it
|
|
10
|
+
* finishes. Refresh what it changed here. With `@panneau/data/query`:
|
|
11
|
+
* `invalidateActionResult(client, resource, result, target.selection)`.
|
|
12
|
+
*/
|
|
13
|
+
onResult?: (result: ActionResult, context: ActionResultContext) => unknown;
|
|
14
|
+
/**
|
|
15
|
+
* Goes to the `redirect` of a result, or to the URL or route of a `link` action. Defaults to
|
|
16
|
+
* `window.location.assign()` for URLs; routes need it.
|
|
17
|
+
*/
|
|
18
|
+
onNavigate?: (to: string | RouteReference) => void;
|
|
19
|
+
/** Opens the `download` of a result. Defaults to a link to its URL, clicked. */
|
|
20
|
+
onDownload?: (download: ActionDownload) => void;
|
|
21
|
+
/** Handlers of `client` actions by their `component`, over those of the plugins. */
|
|
22
|
+
clientActions?: Readonly<Record<string, ClientActionHandler>>;
|
|
23
|
+
/** Milliseconds between two checks of a queued run. Defaults to 2000. */
|
|
24
|
+
pollInterval?: number;
|
|
25
|
+
/** Locales of localized parameters. Defaults to the definition's. */
|
|
26
|
+
locales?: readonly string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Runs the actions that lists and forms hand over ([decision 0023]): it asks for their parameters
|
|
30
|
+
* in a dialog, runs them through the adapter, shows their result (a toast, and the items that
|
|
31
|
+
* didn't succeed), follows queued runs until they end, refreshes through `onResult`, follows
|
|
32
|
+
* redirects and opens downloads. Put it inside `PanneauRoot`, and above the router of an app, so
|
|
33
|
+
* that runs are followed from page to page. `useActionRunner(resource)` runs an action.
|
|
34
|
+
*/
|
|
35
|
+
export declare function ActionRunnerProvider({ adapter, children, onResult, onNavigate, onDownload, clientActions, pollInterval, locales, }: ActionRunnerProviderProps): import("react").JSX.Element;
|
|
36
|
+
//# sourceMappingURL=ActionRunnerProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionRunnerProvider.d.ts","sourceRoot":"","sources":["../src/ActionRunnerProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA+C,MAAM,OAAO,CAAC;AAGpF,OAAO,KAAK,EAAE,YAAY,EAAa,cAAc,EAAE,MAAM,mBAAmB,CAAC;AASjF,OAAO,KAAK,EACR,aAAa,EACb,cAAc,EAEd,mBAAmB,EAEnB,mBAAmB,EAEtB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,yBAAyB;IACtC,gFAAgF;IAChF,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC;IAC3E;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,KAAK,IAAI,CAAC;IACnD,gFAAgF;IAChF,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;IAChD,oFAAoF;IACpF,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAC9D,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;AAmCD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,EACjC,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,UAAuB,EACvB,UAAyB,EACzB,aAAa,EACb,YAAmB,EACnB,OAAO,GACV,EAAE,yBAAyB,+BA+O3B"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { messages } from "./messages.js";
|
|
2
|
+
import { ParametersDialog } from "./ParametersDialog.js";
|
|
3
|
+
import { buildActionRequest, getLinkTarget, isFinished } from "./request.js";
|
|
4
|
+
import { describeResult } from "./describe.js";
|
|
5
|
+
import { ResultsDialog } from "./ResultsDialog.js";
|
|
6
|
+
import { ActionRunnerContext } from "./context.js";
|
|
7
|
+
import { useEffect, useEffectEvent, useRef, useState } from "react";
|
|
8
|
+
import { usePlugins } from "@panneau/core";
|
|
9
|
+
import { usePanneauIntl, useToast } from "@panneau/ui";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
//#region src/ActionRunnerProvider.tsx
|
|
12
|
+
function navigateTo(to) {
|
|
13
|
+
if (typeof to === "string") window.location.assign(to);
|
|
14
|
+
else console.warn(`[panneau] An action goes to the route "${to.route}": give ActionRunnerProvider an onNavigate.`);
|
|
15
|
+
}
|
|
16
|
+
function openDownload(download) {
|
|
17
|
+
const link = document.createElement("a");
|
|
18
|
+
link.href = download.url;
|
|
19
|
+
if (download.filename !== void 0) link.download = download.filename;
|
|
20
|
+
link.rel = "noopener";
|
|
21
|
+
link.hidden = true;
|
|
22
|
+
document.body.append(link);
|
|
23
|
+
link.click();
|
|
24
|
+
link.remove();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Runs the actions that lists and forms hand over ([decision 0023]): it asks for their parameters
|
|
28
|
+
* in a dialog, runs them through the adapter, shows their result (a toast, and the items that
|
|
29
|
+
* didn't succeed), follows queued runs until they end, refreshes through `onResult`, follows
|
|
30
|
+
* redirects and opens downloads. Put it inside `PanneauRoot`, and above the router of an app, so
|
|
31
|
+
* that runs are followed from page to page. `useActionRunner(resource)` runs an action.
|
|
32
|
+
*/
|
|
33
|
+
function ActionRunnerProvider({ adapter, children, onResult, onNavigate = navigateTo, onDownload = openDownload, clientActions, pollInterval = 2e3, locales }) {
|
|
34
|
+
const intl = usePanneauIntl();
|
|
35
|
+
const toasts = useToast();
|
|
36
|
+
const plugins = usePlugins();
|
|
37
|
+
const [parameters, setParameters] = useState(null);
|
|
38
|
+
const [client, setClient] = useState(null);
|
|
39
|
+
const [details, setDetails] = useState(null);
|
|
40
|
+
const [runs, setRuns] = useState([]);
|
|
41
|
+
const notified = useRef(/* @__PURE__ */ new Set());
|
|
42
|
+
const finish = async (resource, target, result) => {
|
|
43
|
+
await onResult?.(result, {
|
|
44
|
+
resource,
|
|
45
|
+
target
|
|
46
|
+
});
|
|
47
|
+
const content = describeResult(result, target.action.label, intl);
|
|
48
|
+
const hasDetails = (result.results ?? []).length > 0;
|
|
49
|
+
const download = result.download ?? null;
|
|
50
|
+
if (download !== null) onDownload(download);
|
|
51
|
+
toasts.show({
|
|
52
|
+
...content,
|
|
53
|
+
...hasDetails ? { action: {
|
|
54
|
+
label: intl.formatMessage(messages.details),
|
|
55
|
+
onAction: () => setDetails({
|
|
56
|
+
label: target.action.label,
|
|
57
|
+
result
|
|
58
|
+
})
|
|
59
|
+
} } : download !== null ? { action: {
|
|
60
|
+
label: intl.formatMessage(messages.download),
|
|
61
|
+
onAction: () => onDownload(download)
|
|
62
|
+
} } : {}
|
|
63
|
+
});
|
|
64
|
+
const redirect = result.redirect ?? null;
|
|
65
|
+
if (redirect !== null) onNavigate(redirect);
|
|
66
|
+
};
|
|
67
|
+
const follow = (resource_0, target_0, run) => {
|
|
68
|
+
setRuns((current) => [...current.filter((entry) => entry.run.id !== run.id), {
|
|
69
|
+
run,
|
|
70
|
+
resource: resource_0,
|
|
71
|
+
target: target_0
|
|
72
|
+
}]);
|
|
73
|
+
toasts.show({
|
|
74
|
+
title: intl.formatMessage(messages.queued, { action: target_0.action.label }),
|
|
75
|
+
description: intl.formatMessage(messages.queuedDescription),
|
|
76
|
+
tone: "info"
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
const ended = async (entry_0) => {
|
|
80
|
+
const { run: run_0, resource: resource_1, target: target_1 } = entry_0;
|
|
81
|
+
if (notified.current.has(run_0.id)) return;
|
|
82
|
+
notified.current.add(run_0.id);
|
|
83
|
+
if (run_0.result !== void 0) await finish(resource_1, target_1, run_0.result);
|
|
84
|
+
else if (run_0.status === "cancelled") toasts.show({ title: intl.formatMessage(messages.cancelled, { action: target_1.action.label }) });
|
|
85
|
+
else if (run_0.status === "failed") toasts.show({
|
|
86
|
+
title: intl.formatMessage(messages.failed, { action: target_1.action.label }),
|
|
87
|
+
tone: "danger"
|
|
88
|
+
});
|
|
89
|
+
else toasts.show({
|
|
90
|
+
title: intl.formatMessage(messages.done, { action: target_1.action.label }),
|
|
91
|
+
tone: "success"
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
const execute = async (resource_2, target_2, values = {}) => {
|
|
95
|
+
const response = await adapter.runAction(resource_2, target_2.action.id, buildActionRequest(target_2, values));
|
|
96
|
+
if (response.run !== void 0) {
|
|
97
|
+
follow(resource_2, target_2, response.run);
|
|
98
|
+
return {
|
|
99
|
+
status: "queued",
|
|
100
|
+
run: response.run
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
await finish(resource_2, target_2, response.result);
|
|
104
|
+
return {
|
|
105
|
+
status: "done",
|
|
106
|
+
result: response.result
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
const run_1 = (resource_3, target_3) => {
|
|
110
|
+
const { action } = target_3;
|
|
111
|
+
if (action.kind === "link") {
|
|
112
|
+
const to = getLinkTarget(target_3);
|
|
113
|
+
if (to === null) return Promise.reject(/* @__PURE__ */ new Error(`The action "${action.id}" has no URL or route.`));
|
|
114
|
+
onNavigate(to);
|
|
115
|
+
return Promise.resolve({
|
|
116
|
+
status: "navigated",
|
|
117
|
+
to
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (action.kind === "client") {
|
|
121
|
+
const name = action.component ?? "";
|
|
122
|
+
const Handler = (clientActions !== void 0 && Object.hasOwn(clientActions, name) ? clientActions[name] : void 0) ?? plugins.actions.get(name);
|
|
123
|
+
if (Handler === void 0) return Promise.reject(/* @__PURE__ */ new Error(`No handler for the client action "${action.id}" (${name}).`));
|
|
124
|
+
return new Promise((resolve) => setClient({
|
|
125
|
+
resource: resource_3,
|
|
126
|
+
target: target_3,
|
|
127
|
+
resolve,
|
|
128
|
+
Handler
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
if ((action.fields ?? []).length > 0) return new Promise((resolve_0) => setParameters({
|
|
132
|
+
resource: resource_3,
|
|
133
|
+
target: target_3,
|
|
134
|
+
resolve: resolve_0
|
|
135
|
+
}));
|
|
136
|
+
return execute(resource_3, target_3);
|
|
137
|
+
};
|
|
138
|
+
const cancelRun = async (id) => {
|
|
139
|
+
if (adapter.cancelActionRun === void 0) throw new Error("The adapter cannot cancel runs.");
|
|
140
|
+
await adapter.cancelActionRun(id);
|
|
141
|
+
const updated = await adapter.getActionRun(id);
|
|
142
|
+
setRuns((current_0) => current_0.map((entry_1) => entry_1.run.id === id ? {
|
|
143
|
+
...entry_1,
|
|
144
|
+
run: updated
|
|
145
|
+
} : entry_1));
|
|
146
|
+
const entry_2 = runs.find((candidate) => candidate.run.id === id);
|
|
147
|
+
if (entry_2 !== void 0 && isFinished(updated.status)) await ended({
|
|
148
|
+
...entry_2,
|
|
149
|
+
run: updated
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
const dismissRun = (id_0) => setRuns((current_1) => current_1.filter((entry_3) => entry_3.run.id !== id_0));
|
|
153
|
+
const onRunEnded = useEffectEvent((entry_4) => ended(entry_4));
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
const active = runs.filter((entry_5) => !isFinished(entry_5.run.status));
|
|
156
|
+
if (active.length === 0) return;
|
|
157
|
+
let stopped = false;
|
|
158
|
+
const timer = setTimeout(() => {
|
|
159
|
+
Promise.all(active.map(async (entry_6) => {
|
|
160
|
+
try {
|
|
161
|
+
const next = await adapter.getActionRun(entry_6.run.id);
|
|
162
|
+
if (stopped) return;
|
|
163
|
+
setRuns((current_3) => current_3.map((candidate_0) => candidate_0.run.id === next.id ? {
|
|
164
|
+
...candidate_0,
|
|
165
|
+
run: next
|
|
166
|
+
} : candidate_0));
|
|
167
|
+
if (isFinished(next.status)) await onRunEnded({
|
|
168
|
+
...entry_6,
|
|
169
|
+
run: next
|
|
170
|
+
});
|
|
171
|
+
} catch {
|
|
172
|
+
if (!stopped) setRuns((current_2) => [...current_2]);
|
|
173
|
+
}
|
|
174
|
+
}));
|
|
175
|
+
}, pollInterval);
|
|
176
|
+
return () => {
|
|
177
|
+
stopped = true;
|
|
178
|
+
clearTimeout(timer);
|
|
179
|
+
};
|
|
180
|
+
}, [
|
|
181
|
+
runs,
|
|
182
|
+
adapter,
|
|
183
|
+
pollInterval
|
|
184
|
+
]);
|
|
185
|
+
const value = {
|
|
186
|
+
adapter,
|
|
187
|
+
run: run_1,
|
|
188
|
+
runs,
|
|
189
|
+
cancelRun,
|
|
190
|
+
dismissRun
|
|
191
|
+
};
|
|
192
|
+
const ClientHandler = client?.Handler;
|
|
193
|
+
return /* @__PURE__ */ jsxs(ActionRunnerContext, {
|
|
194
|
+
value,
|
|
195
|
+
children: [
|
|
196
|
+
children,
|
|
197
|
+
parameters !== null ? /* @__PURE__ */ jsx(ParametersDialog, {
|
|
198
|
+
action: parameters.target.action,
|
|
199
|
+
count: parameters.target.count,
|
|
200
|
+
locales,
|
|
201
|
+
onSubmit: (values_0) => execute(parameters.resource, parameters.target, values_0),
|
|
202
|
+
onDone: (outcome) => {
|
|
203
|
+
setParameters(null);
|
|
204
|
+
parameters.resolve(outcome);
|
|
205
|
+
},
|
|
206
|
+
onCancel: () => {
|
|
207
|
+
setParameters(null);
|
|
208
|
+
parameters.resolve(false);
|
|
209
|
+
}
|
|
210
|
+
}) : null,
|
|
211
|
+
client !== null && ClientHandler !== void 0 ? /* @__PURE__ */ jsx(ClientHandler, {
|
|
212
|
+
action: client.target.action,
|
|
213
|
+
resource: client.resource,
|
|
214
|
+
target: client.target,
|
|
215
|
+
onDone: (result_0) => {
|
|
216
|
+
setClient(null);
|
|
217
|
+
if (result_0 === void 0) {
|
|
218
|
+
client.resolve({
|
|
219
|
+
status: "done",
|
|
220
|
+
result: {}
|
|
221
|
+
});
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
finish(client.resource, client.target, result_0).then(() => client.resolve({
|
|
225
|
+
status: "done",
|
|
226
|
+
result: result_0
|
|
227
|
+
}));
|
|
228
|
+
},
|
|
229
|
+
onCancel: () => {
|
|
230
|
+
setClient(null);
|
|
231
|
+
client.resolve(false);
|
|
232
|
+
}
|
|
233
|
+
}) : null,
|
|
234
|
+
details !== null ? /* @__PURE__ */ jsx(ResultsDialog, {
|
|
235
|
+
label: details.label,
|
|
236
|
+
result: details.result,
|
|
237
|
+
onClose: () => setDetails(null)
|
|
238
|
+
}) : null
|
|
239
|
+
]
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
//#endregion
|
|
243
|
+
export { ActionRunnerProvider };
|
|
244
|
+
|
|
245
|
+
//# sourceMappingURL=ActionRunnerProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionRunnerProvider.js","names":["resource","target","entry","run","resolve","current","id","candidate","values","result"],"sources":["../src/ActionRunnerProvider.tsx"],"sourcesContent":["import { type ReactNode, useEffect, useEffectEvent, useRef, useState } from 'react';\n\nimport { usePlugins } from '@panneau/core';\nimport type { ActionResult, ActionRun, RouteReference } from '@panneau/protocol';\nimport { usePanneauIntl, useToast } from '@panneau/ui';\n\nimport { ParametersDialog } from './ParametersDialog';\nimport { ResultsDialog } from './ResultsDialog';\nimport { ActionRunnerContext, type ActionRunnerContextValue } from './context';\nimport { describeResult } from './describe';\nimport { messages } from './messages';\nimport { buildActionRequest, getLinkTarget, isFinished } from './request';\nimport type {\n ActionAdapter,\n ActionDownload,\n ActionOutcome,\n ActionResultContext,\n ActionTarget,\n ClientActionHandler,\n FollowedRun,\n} from './types';\n\nexport interface ActionRunnerProviderProps {\n /** Runs the actions: a `DataAdapter`, or any object with its action methods. */\n adapter: ActionAdapter;\n children?: ReactNode;\n /**\n * Called with every result: of an action that finished at once, and of a queued run when it\n * finishes. Refresh what it changed here. With `@panneau/data/query`:\n * `invalidateActionResult(client, resource, result, target.selection)`.\n */\n onResult?: (result: ActionResult, context: ActionResultContext) => unknown;\n /**\n * Goes to the `redirect` of a result, or to the URL or route of a `link` action. Defaults to\n * `window.location.assign()` for URLs; routes need it.\n */\n onNavigate?: (to: string | RouteReference) => void;\n /** Opens the `download` of a result. Defaults to a link to its URL, clicked. */\n onDownload?: (download: ActionDownload) => void;\n /** Handlers of `client` actions by their `component`, over those of the plugins. */\n clientActions?: Readonly<Record<string, ClientActionHandler>>;\n /** Milliseconds between two checks of a queued run. Defaults to 2000. */\n pollInterval?: number;\n /** Locales of localized parameters. Defaults to the definition's. */\n locales?: readonly string[];\n}\n\ninterface Pending {\n resource: string;\n target: ActionTarget;\n resolve: (outcome: ActionOutcome | false) => void;\n}\n\ninterface ClientPending extends Pending {\n Handler: ClientActionHandler;\n}\n\nfunction navigateTo(to: string | RouteReference) {\n if (typeof to === 'string') {\n window.location.assign(to);\n } else {\n console.warn(\n `[panneau] An action goes to the route \"${to.route}\": give ActionRunnerProvider an onNavigate.`,\n );\n }\n}\n\nfunction openDownload(download: ActionDownload) {\n const link = document.createElement('a');\n link.href = download.url;\n if (download.filename !== undefined) {\n link.download = download.filename;\n }\n link.rel = 'noopener';\n link.hidden = true;\n document.body.append(link);\n link.click();\n link.remove();\n}\n\n/**\n * Runs the actions that lists and forms hand over ([decision 0023]): it asks for their parameters\n * in a dialog, runs them through the adapter, shows their result (a toast, and the items that\n * didn't succeed), follows queued runs until they end, refreshes through `onResult`, follows\n * redirects and opens downloads. Put it inside `PanneauRoot`, and above the router of an app, so\n * that runs are followed from page to page. `useActionRunner(resource)` runs an action.\n */\nexport function ActionRunnerProvider({\n adapter,\n children,\n onResult,\n onNavigate = navigateTo,\n onDownload = openDownload,\n clientActions,\n pollInterval = 2000,\n locales,\n}: ActionRunnerProviderProps) {\n const intl = usePanneauIntl();\n const toasts = useToast();\n const plugins = usePlugins();\n const [parameters, setParameters] = useState<Pending | null>(null);\n const [client, setClient] = useState<ClientPending | null>(null);\n const [details, setDetails] = useState<{ label: string; result: ActionResult } | null>(null);\n const [runs, setRuns] = useState<readonly FollowedRun[]>([]);\n // Runs whose end was already shown, so that a late poll doesn't show it twice.\n const notified = useRef(new Set<string>());\n\n const finish = async (resource: string, target: ActionTarget, result: ActionResult) => {\n await onResult?.(result, { resource, target });\n const content = describeResult(result, target.action.label, intl);\n const hasDetails = (result.results ?? []).length > 0;\n const download = result.download ?? null;\n if (download !== null) {\n onDownload(download);\n }\n toasts.show({\n ...content,\n ...(hasDetails\n ? {\n action: {\n label: intl.formatMessage(messages.details),\n onAction: () => setDetails({ label: target.action.label, result }),\n },\n }\n : download !== null\n ? {\n action: {\n label: intl.formatMessage(messages.download),\n onAction: () => onDownload(download),\n },\n }\n : {}),\n });\n const redirect = result.redirect ?? null;\n if (redirect !== null) {\n onNavigate(redirect);\n }\n };\n\n const follow = (resource: string, target: ActionTarget, run: ActionRun) => {\n setRuns((current) => [\n ...current.filter((entry) => entry.run.id !== run.id),\n { run, resource, target },\n ]);\n toasts.show({\n title: intl.formatMessage(messages.queued, { action: target.action.label }),\n description: intl.formatMessage(messages.queuedDescription),\n tone: 'info',\n });\n };\n\n const ended = async (entry: FollowedRun) => {\n const { run, resource, target } = entry;\n if (notified.current.has(run.id)) {\n return;\n }\n notified.current.add(run.id);\n if (run.result !== undefined) {\n await finish(resource, target, run.result);\n } else if (run.status === 'cancelled') {\n toasts.show({\n title: intl.formatMessage(messages.cancelled, { action: target.action.label }),\n });\n } else if (run.status === 'failed') {\n toasts.show({\n title: intl.formatMessage(messages.failed, { action: target.action.label }),\n tone: 'danger',\n });\n } else {\n toasts.show({\n title: intl.formatMessage(messages.done, { action: target.action.label }),\n tone: 'success',\n });\n }\n };\n\n const execute = async (\n resource: string,\n target: ActionTarget,\n values: Record<string, unknown> = {},\n ): Promise<ActionOutcome> => {\n const response = await adapter.runAction(\n resource,\n target.action.id,\n buildActionRequest(target, values),\n );\n if (response.run !== undefined) {\n follow(resource, target, response.run);\n return { status: 'queued', run: response.run };\n }\n await finish(resource, target, response.result);\n return { status: 'done', result: response.result };\n };\n\n const run = (resource: string, target: ActionTarget): Promise<ActionOutcome | false> => {\n const { action } = target;\n if (action.kind === 'link') {\n const to = getLinkTarget(target);\n if (to === null) {\n return Promise.reject(new Error(`The action \"${action.id}\" has no URL or route.`));\n }\n onNavigate(to);\n return Promise.resolve({ status: 'navigated', to });\n }\n if (action.kind === 'client') {\n const name = action.component ?? '';\n const Handler =\n (clientActions !== undefined && Object.hasOwn(clientActions, name)\n ? clientActions[name]\n : undefined) ?? (plugins.actions.get(name) as ClientActionHandler | undefined);\n if (Handler === undefined) {\n return Promise.reject(\n new Error(`No handler for the client action \"${action.id}\" (${name}).`),\n );\n }\n return new Promise((resolve) => setClient({ resource, target, resolve, Handler }));\n }\n if ((action.fields ?? []).length > 0) {\n return new Promise((resolve) => setParameters({ resource, target, resolve }));\n }\n return execute(resource, target);\n };\n\n const cancelRun = async (id: string) => {\n if (adapter.cancelActionRun === undefined) {\n throw new Error('The adapter cannot cancel runs.');\n }\n await adapter.cancelActionRun(id);\n const updated = await adapter.getActionRun(id);\n setRuns((current) =>\n current.map((entry) => (entry.run.id === id ? { ...entry, run: updated } : entry)),\n );\n const entry = runs.find((candidate) => candidate.run.id === id);\n if (entry !== undefined && isFinished(updated.status)) {\n await ended({ ...entry, run: updated });\n }\n };\n\n const dismissRun = (id: string) =>\n setRuns((current) => current.filter((entry) => entry.run.id !== id));\n\n const onRunEnded = useEffectEvent((entry: FollowedRun) => ended(entry));\n\n // Checks the unfinished runs, one interval after the last change of the runs.\n useEffect(() => {\n const active = runs.filter((entry) => !isFinished(entry.run.status));\n if (active.length === 0) {\n return undefined;\n }\n let stopped = false;\n const timer = setTimeout(() => {\n void Promise.all(\n active.map(async (entry) => {\n try {\n const next = await adapter.getActionRun(entry.run.id);\n if (stopped) {\n return;\n }\n setRuns((current) =>\n current.map((candidate) =>\n candidate.run.id === next.id\n ? { ...candidate, run: next }\n : candidate,\n ),\n );\n if (isFinished(next.status)) {\n await onRunEnded({ ...entry, run: next });\n }\n } catch {\n // Checked again at the next interval.\n if (!stopped) {\n setRuns((current) => [...current]);\n }\n }\n }),\n );\n }, pollInterval);\n return () => {\n stopped = true;\n clearTimeout(timer);\n };\n }, [runs, adapter, pollInterval]);\n\n const value: ActionRunnerContextValue = { adapter, run, runs, cancelRun, dismissRun };\n\n const ClientHandler = client?.Handler;\n return (\n <ActionRunnerContext value={value}>\n {children}\n {parameters !== null ? (\n <ParametersDialog\n action={parameters.target.action}\n count={parameters.target.count}\n locales={locales}\n onSubmit={(values) => execute(parameters.resource, parameters.target, values)}\n onDone={(outcome) => {\n setParameters(null);\n parameters.resolve(outcome);\n }}\n onCancel={() => {\n setParameters(null);\n parameters.resolve(false);\n }}\n />\n ) : null}\n {client !== null && ClientHandler !== undefined ? (\n <ClientHandler\n action={client.target.action}\n resource={client.resource}\n target={client.target}\n onDone={(result) => {\n setClient(null);\n if (result === undefined) {\n client.resolve({ status: 'done', result: {} });\n return;\n }\n void finish(client.resource, client.target, result).then(() =>\n client.resolve({ status: 'done', result }),\n );\n }}\n onCancel={() => {\n setClient(null);\n client.resolve(false);\n }}\n />\n ) : null}\n {details !== null ? (\n <ResultsDialog\n label={details.label}\n result={details.result}\n onClose={() => setDetails(null)}\n />\n ) : null}\n </ActionRunnerContext>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ActionRunsProps {
|
|
2
|
+
/** Accessible name of the list. Defaults to "Actions in progress". */
|
|
3
|
+
'aria-label'?: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The queued runs that the closest `ActionRunnerProvider` follows: their status and progress, a
|
|
8
|
+
* button to cancel those that haven't finished (when the adapter can), and one to dismiss those
|
|
9
|
+
* that have. For a notification area. Renders nothing without runs.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ActionRuns({ 'aria-label': ariaLabel, className }: ActionRunsProps): import("react").JSX.Element | null;
|
|
12
|
+
//# sourceMappingURL=ActionRuns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionRuns.d.ts","sourceRoot":"","sources":["../src/ActionRuns.tsx"],"names":[],"mappings":"AAqBA,MAAM,WAAW,eAAe;IAC5B,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,eAAe,sCA8EjF"}
|