@chengxianglibra/dsh-data-analysis 0.3.0 → 0.3.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/README.en.md +7 -2
- package/README.md +7 -2
- package/assets/logo-horizontal.svg +42 -0
- package/assets/logo-mark.svg +20 -0
- package/lib/bin/environment.js +99 -76
- package/lib/client/analysis-ui/offline-runtime.generated.js +1 -1
- package/lib/client/credentials/install.js +42 -38
- package/lib/client/credentials/model.js +9 -0
- package/lib/client/credentials/report-publishing.js +84 -0
- package/lib/client/credentials/styles.js +2 -0
- package/lib/client/i18n/credentials.js +18 -0
- package/lib/client/i18n/presentation.js +24 -0
- package/lib/client/right-tabs/install.js +9 -1
- package/lib/client/right-tabs/report-actions.js +30 -4
- package/lib/client/right-tabs/report-publishing.js +107 -0
- package/lib/client/right-tabs/styles.js +3 -0
- package/lib/client/ui/styles.generated.js +3 -0
- package/lib/client-composition.js +202 -191
- package/lib/client.js +205 -194
- package/lib/datasource/resolver-program.js +4 -4
- package/lib/environment/binding.js +16 -23
- package/lib/environment/doctor.js +8 -1
- package/lib/environment/identity.js +32 -0
- package/lib/environment/project-preparation.js +5 -5
- package/lib/environment/runtime.js +15 -16
- package/lib/plugin-agents.js +6 -1
- package/lib/plugin.js +18 -0
- package/lib/presentation/rpc.js +19 -0
- package/lib/report-publishing/adapters.js +136 -0
- package/lib/report-publishing/config.js +58 -0
- package/lib/report-publishing/credential-contract.js +25 -0
- package/lib/report-publishing/service.js +133 -0
- package/lib/skill-build.generated.js +2 -2
- package/lib/types/client/credentials/install.d.ts +2 -1
- package/lib/types/client/credentials/model.d.ts +30 -0
- package/lib/types/client/credentials/report-publishing.d.ts +5 -0
- package/lib/types/client/credentials/styles.d.ts +1 -1
- package/lib/types/client/i18n/copy.d.ts +21 -0
- package/lib/types/client/i18n/credentials.d.ts +18 -0
- package/lib/types/client/i18n/presentation.d.ts +24 -0
- package/lib/types/client/right-tabs/report-actions.d.ts +5 -1
- package/lib/types/client/right-tabs/report-publishing.d.ts +21 -0
- package/lib/types/client/right-tabs/styles.d.ts +1 -1
- package/lib/types/environment/identity.d.ts +6 -0
- package/lib/types/plugin-agents.d.ts +2 -0
- package/lib/types/plugin.d.ts +4 -0
- package/lib/types/presentation/rpc.d.ts +1358 -0
- package/lib/types/report-publishing/adapters.d.ts +6 -0
- package/lib/types/report-publishing/config.d.ts +23 -0
- package/lib/types/report-publishing/credential-contract.d.ts +61 -0
- package/lib/types/report-publishing/service.d.ts +31 -0
- package/package.json +4 -1
- package/skills/dsh-data-analysis-files/references/delivery.md +3 -1
|
@@ -13,6 +13,7 @@ import { Input } from "../ui/primitives/input.js";
|
|
|
13
13
|
import { TabsContent, TabsList, Tabs as TabsRoot, TabsTrigger } from "../ui/primitives/tabs.js";
|
|
14
14
|
import { UIStyleSheet } from "../ui/theme.js";
|
|
15
15
|
import { CredentialClientModel, credentialMessage } from "./model.js";
|
|
16
|
+
import { ReportPublishingCredentials } from "./report-publishing.js";
|
|
16
17
|
import { credentialStyles } from "./styles.js";
|
|
17
18
|
const statusLabels = {
|
|
18
19
|
'awaiting-input': 'marivo.credentials.awaiting-input',
|
|
@@ -107,14 +108,16 @@ function CredentialForm({ context, request, state, model, workspaceId }) {
|
|
|
107
108
|
}, children: t('marivo.credentials.confirm-saved-value-deletion') }), _jsx(Button, { variant: "outline", type: "button", onClick: () => setDeleting(''), children: t('marivo.credentials.keep') })] }) }))] }) }, ref));
|
|
108
109
|
}) }) }))] }), _jsx(OperationOutcome, { entry: outcome })] }), !ended && (request || busy) && (_jsxs(CardFooter, { className: "mc-form-footer", children: [request && (_jsx(Button, { variant: "default", type: "button", disabled: busy, onClick: submit, children: t('marivo.credentials.submit-credentials-and-continue') })), request ? (_jsx(Button, { variant: "ghost", className: "mc-cancel", type: "button", onClick: () => void model.cancelRequest(request.id), children: t('marivo.credentials.cancel-this-round') })) : (busy && (_jsx(Button, { variant: "ghost", className: "mc-cancel", type: "button", onClick: () => void model.cancelOperation(context.token), children: t('marivo.credentials.cancel-operation') })))] })), ended && (_jsx(CardFooter, { className: "mc-form-footer", children: _jsx(Button, { variant: "outline", type: "button", onClick: () => model.show(workspaceId), children: t('marivo.credentials.back-to-datasource-management') }) }))] }));
|
|
109
110
|
}
|
|
110
|
-
export function CredentialPanel({ model, workspaces, onRefresh }) {
|
|
111
|
+
export function CredentialPanel({ model, workspaces, onRefresh, initialSection = 'datasources' }) {
|
|
111
112
|
const t = useCopy();
|
|
112
113
|
const formId = useId();
|
|
113
114
|
const state = useSyncExternalStore(model.subscribe, model.getSnapshot);
|
|
115
|
+
const [section, setSection] = useState(initialSection);
|
|
114
116
|
const columns = useRef(null);
|
|
115
117
|
const navigation = useRef(null);
|
|
116
118
|
const main = useRef(null);
|
|
117
119
|
const request = state.requests.find((item) => item.id === state.requestId);
|
|
120
|
+
const activeSection = request || !state.workspaceId ? 'datasources' : section;
|
|
118
121
|
const pending = state.requests.filter((item) => item.endedAt === undefined && item.sessionId === state.sessionId);
|
|
119
122
|
const context = request
|
|
120
123
|
? request.context
|
|
@@ -139,7 +142,7 @@ export function CredentialPanel({ model, workspaces, onRefresh }) {
|
|
|
139
142
|
else if (bounds.right > viewport.right)
|
|
140
143
|
list.scrollLeft += bounds.right - viewport.right;
|
|
141
144
|
}
|
|
142
|
-
}, [context?.token]);
|
|
145
|
+
}, [context?.token, activeSection]);
|
|
143
146
|
const workspaceId = request
|
|
144
147
|
? (workspaces.find((item) => item.sessionIds.includes(request.sessionId))?.workspaceId ?? '')
|
|
145
148
|
: state.workspaceId;
|
|
@@ -149,42 +152,43 @@ export function CredentialPanel({ model, workspaces, onRefresh }) {
|
|
|
149
152
|
if (!state.open)
|
|
150
153
|
return null;
|
|
151
154
|
const credentialForm = context && (_jsx(CredentialForm, { context: context, request: request, state: state, model: model, workspaceId: workspaceId }, context.token));
|
|
152
|
-
return (_jsxs("section", { className: "mv-ui mc-panel", "aria-label": t('marivo.credentials.datasources-and-credentials'), children: [_jsx(UIStyleSheet, {}), _jsx("style", { children: credentialStyles }), _jsxs("div", { className: "rt-heading-row", children: [_jsx("h2", { className: "rt-heading", children: t('marivo.credentials.datasources-and-credentials') }), _jsx("div", { className: "mc-heading-actions", children: _jsx(IconButton, { variant: "ghost", className: "mc-icon-button", type: "button", title: t('marivo.credentials.refresh-datasources'), label: t('marivo.credentials.refresh-datasources'), disabled: state.loading, onClick: onRefresh ?? (() => void model.refreshDatasources()), children: _jsx(CredentialIcon, { name: "refresh" }) }) })] }), _jsx("p", { className: "rt-caption", children: workspaces.find((item) => item.workspaceId === workspaceId)?.title ?? workspaceId }), _jsxs("
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
155
|
+
return (_jsxs("section", { className: "mv-ui mc-panel", "aria-label": t('marivo.credentials.datasources-and-credentials'), children: [_jsx(UIStyleSheet, {}), _jsx("style", { children: credentialStyles }), _jsxs("div", { className: "rt-heading-row", children: [_jsx("h2", { className: "rt-heading", children: t('marivo.credentials.datasources-and-credentials') }), activeSection === 'datasources' && (_jsx("div", { className: "mc-heading-actions", children: _jsx(IconButton, { variant: "ghost", className: "mc-icon-button", type: "button", title: t('marivo.credentials.refresh-datasources'), label: t('marivo.credentials.refresh-datasources'), disabled: state.loading, onClick: onRefresh ?? (() => void model.refreshDatasources()), children: _jsx(CredentialIcon, { name: "refresh" }) }) }))] }), _jsx("p", { className: "rt-caption", children: workspaces.find((item) => item.workspaceId === workspaceId)?.title ?? workspaceId }), _jsxs(TabsRoot, { className: "mc-shell", value: activeSection, onValueChange: setSection, children: [!request && (_jsxs(TabsList, { "aria-label": t('marivo.credentials.datasources-and-credentials'), children: [_jsx(TabsTrigger, { value: "datasources", children: t('marivo.credentials.datasources-tab') }), _jsx(TabsTrigger, { value: "credentials", disabled: !workspaceId, children: t('marivo.credentials.credential-management-tab') })] })), _jsxs(TabsContent, { value: "datasources", className: "mc-directory-content", children: [(pending.length > 0 || request) && (_jsxs("div", { className: "mc-requests", children: [_jsx("span", { children: pending.length
|
|
156
|
+
? t('marivo.credentials.requests-in-this-session')
|
|
157
|
+
: t('marivo.credentials.configuration-result') }), pending.map((item) => (_jsxs(Button, { variant: "outline", type: "button", onClick: () => model.openRequest(item.id), children: [item.context?.name, " \u00B7 ", t(statusLabels[item.status])] }, item.id))), request && (_jsx(Button, { variant: "ghost", type: "button", onClick: () => model.show(workspaceId), children: t('marivo.credentials.datasource-management') }))] })), _jsxs(TabsRoot, { className: "mc-columns", ref: columns, value: !request ? (context?.token ?? '') : '', activationMode: "manual", onValueChange: (token) => {
|
|
158
|
+
model.select(token);
|
|
159
|
+
}, children: [!request && (_jsx("aside", { className: "mc-nav", "aria-label": t('marivo.credentials.datasource-navigation'), children: _jsxs("div", { children: [_jsx("div", { className: "mc-nav-heading", children: _jsx("h3", { children: request
|
|
160
|
+
? t('marivo.credentials.requested-datasource')
|
|
161
|
+
: t('marivo.credentials.datasource-value', { p0: datasources.length }) }) }), _jsx(TabsList, { variant: "line", className: "mc-datasources", ref: navigation, "aria-label": t('marivo.credentials.datasource-navigation'), children: datasources.map((item, index) => {
|
|
162
|
+
const count = item.refs.filter((ref) => item.credentials[ref]?.configured).length;
|
|
163
|
+
const ready = count === item.refs.length;
|
|
164
|
+
const running = state.operations.some((entry) => entry.handle.scope === item.token);
|
|
165
|
+
const status = item.refs.length === 0
|
|
166
|
+
? t('marivo.credentials.no-credentials-required')
|
|
167
|
+
: ready
|
|
168
|
+
? t('marivo.credentials.all-credentials-configured')
|
|
169
|
+
: t('marivo.credentials.value-value-configured', {
|
|
170
|
+
p0: count,
|
|
171
|
+
p1: item.refs.length,
|
|
172
|
+
});
|
|
173
|
+
const description = running
|
|
174
|
+
? `${status} · ${t('marivo.credentials.processing')}`
|
|
175
|
+
: status;
|
|
176
|
+
const statusId = `${formId}-datasource-status-${index}`;
|
|
177
|
+
return (_jsxs(TabsTrigger, { value: item.token, className: "mc-datasource", type: "button", title: item.name, "aria-label": t('marivo.credentials.select-datasource-value', {
|
|
178
|
+
p0: item.name,
|
|
179
|
+
}), "aria-describedby": statusId, children: [_jsx(CredentialIcon, { name: "database" }), _jsxs("span", { className: "mc-datasource-copy", children: [_jsx("span", { className: "mc-datasource-name", children: item.name }), _jsx("span", { className: "mc-dot", "data-ready": ready, title: description, "aria-hidden": "true" })] }), _jsx("span", { id: statusId, className: "mc-visually-hidden", children: description })] }, item.token));
|
|
180
|
+
}) })] }) })), _jsxs("main", { className: "mc-main", ref: main, children: [state.error && (_jsx(Alert, { role: "alert", className: "mc-alert", children: _jsx(AlertDescription, { children: t(state.error) }) })), state.loading && (_jsx("p", { className: "mc-loading", role: "status", children: t('marivo.credentials.loading-datasources-and-credential-status') })), !request && !state.loading && !context && !state.error && (_jsx(Empty, { className: "mc-empty", children: _jsxs(EmptyDescription, { children: [_jsx(CredentialIcon, { name: "database", size: 32 }), _jsx("h3", { children: !state.workspaceId
|
|
181
|
+
? t('marivo.credentials.this-session-is-not-bound-to-a-workspace')
|
|
182
|
+
: state.datasources.length === 0
|
|
183
|
+
? t('marivo.credentials.no-datasources')
|
|
184
|
+
: t('marivo.credentials.select-a-datasource') }), _jsx("p", { children: !state.workspaceId
|
|
185
|
+
? t('marivo.credentials.return-to-the-session-and-retry')
|
|
186
|
+
: state.datasources.length === 0
|
|
187
|
+
? t('marivo.credentials.this-workspace-has-no-defined-datasources')
|
|
188
|
+
: t('marivo.credentials.view-credential-configuration-and-the-latest-connection-test') })] }) })), request
|
|
189
|
+
? credentialForm
|
|
190
|
+
: context && _jsx(TabsContent, { value: context.token, children: credentialForm }), operations.length > 0 && (_jsxs("section", { className: "mc-activity", "aria-label": t('marivo.credentials.credential-operations-in-progress'), children: [_jsxs("p", { className: "mc-activity-heading", children: [t('marivo.credentials.in-progress'), operations.length] }), _jsx("div", { className: "mc-activity-list", children: operations.map((entry) => (_jsxs(Button, { variant: "outline", type: "button", "aria-pressed": state.handle?.id === entry.handle.id, onClick: () => model.selectOperation(entry.handle.id), children: [entry.name, " ", t('marivo.credentials.processing-177')] }, entry.handle.id))) }), activeOperation?.status === 'running' && (_jsxs("div", { className: "mc-progress", children: [_jsxs("p", { children: [state.operations.find((entry) => entry.handle.id === state.handle?.id)
|
|
191
|
+
?.name, ' ', "\u00B7 ", t('marivo.credentials.saving')] }), _jsx(Button, { variant: "outline", type: "button", onClick: () => void model.cancelOperation(), children: t('marivo.credentials.cancel-this-operation') }), activeOperation.saved.length > 0 && (_jsxs("p", { children: [t('marivo.credentials.saved'), activeOperation.saved.join('、'), "\u3002"] }))] }))] }))] })] })] }), _jsx(TabsContent, { value: "credentials", className: "mc-directory-content", children: activeSection === 'credentials' && (_jsx(ReportPublishingCredentials, { model: model, workspaceId: workspaceId }, workspaceId)) })] })] }));
|
|
188
192
|
}
|
|
189
193
|
export function installCredentials(ctx, rpc, changes) {
|
|
190
194
|
let storage;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { credentialRevisionSchema } from "../../datasource/changes-contract.js";
|
|
2
2
|
import { PendingTasks } from "../../lifecycle.js";
|
|
3
|
+
import { publishingCredentialResultSchema, REPORT_PUBLISHING_CHANNEL, } from "../../report-publishing/credential-contract.js";
|
|
3
4
|
import { errorMessage } from "./../i18n/copy.js";
|
|
4
5
|
const CHANNEL = '/dsh-data-analysis-credentials';
|
|
5
6
|
const STORAGE_KEY = 'marivo-credential-operation';
|
|
@@ -118,6 +119,14 @@ export class CredentialClientModel {
|
|
|
118
119
|
throw new CredentialResponseError(result.error?.message ?? '');
|
|
119
120
|
return result.value;
|
|
120
121
|
}
|
|
122
|
+
async publishingCredentials(workspaceId, { endpoint, ...input }, signal) {
|
|
123
|
+
const response = await this.#rpc.call(REPORT_PUBLISHING_CHANNEL, endpoint, { workspaceId, ...input }, signal);
|
|
124
|
+
signal.throwIfAborted();
|
|
125
|
+
const result = publishingCredentialResultSchema.parse(response);
|
|
126
|
+
if (!result.ok)
|
|
127
|
+
throw new CredentialResponseError(result.error.message);
|
|
128
|
+
return result.value;
|
|
129
|
+
}
|
|
121
130
|
async show(workspaceId, selectedToken) {
|
|
122
131
|
this.#suspended = false;
|
|
123
132
|
this.#patch({ open: true, requestId: '' });
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useId, useRef, useState } from 'react';
|
|
3
|
+
import { useCopy } from "../i18n/context.js";
|
|
4
|
+
import { Button } from "../ui/controls.js";
|
|
5
|
+
import { Alert, AlertDescription } from "../ui/primitives/alert.js";
|
|
6
|
+
import { Badge } from "../ui/primitives/badge.js";
|
|
7
|
+
import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle, } from "../ui/primitives/card.js";
|
|
8
|
+
import { Field, FieldGroup, FieldLabel, FieldSet } from "../ui/primitives/field.js";
|
|
9
|
+
import { Input } from "../ui/primitives/input.js";
|
|
10
|
+
const labels = {
|
|
11
|
+
accessKeyId: 'marivo.credentials.scoped-access-key',
|
|
12
|
+
secretAccessKey: 'marivo.credentials.scoped-secret-key',
|
|
13
|
+
sessionToken: 'marivo.credentials.scoped-session-token',
|
|
14
|
+
};
|
|
15
|
+
export function ReportPublishingCredentials({ model, workspaceId, }) {
|
|
16
|
+
const t = useCopy();
|
|
17
|
+
const id = useId();
|
|
18
|
+
const [view, setView] = useState();
|
|
19
|
+
const [editor, setEditor] = useState();
|
|
20
|
+
const [busy, setBusy] = useState(true);
|
|
21
|
+
const [error, setError] = useState('');
|
|
22
|
+
const current = useRef();
|
|
23
|
+
const run = useCallback(async (request) => {
|
|
24
|
+
if (current.current)
|
|
25
|
+
return;
|
|
26
|
+
const controller = new AbortController();
|
|
27
|
+
current.current = controller;
|
|
28
|
+
setEditor(undefined);
|
|
29
|
+
setBusy(true);
|
|
30
|
+
setError('');
|
|
31
|
+
try {
|
|
32
|
+
const next = await model.publishingCredentials(workspaceId, request, controller.signal);
|
|
33
|
+
if (!controller.signal.aborted)
|
|
34
|
+
setView(next);
|
|
35
|
+
}
|
|
36
|
+
catch (failure) {
|
|
37
|
+
if (!controller.signal.aborted) {
|
|
38
|
+
setView(undefined);
|
|
39
|
+
setError(failure instanceof Error &&
|
|
40
|
+
'code' in failure &&
|
|
41
|
+
failure.code === 'report-publishing-config-changed'
|
|
42
|
+
? 'marivo.credentials.publishing-config-changed'
|
|
43
|
+
: 'marivo.credentials.publishing-credential-unconfirmed');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
if (current.current === controller) {
|
|
48
|
+
current.current = undefined;
|
|
49
|
+
setBusy(false);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}, [model, workspaceId]);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
void run({ endpoint: 'describe' });
|
|
55
|
+
return () => {
|
|
56
|
+
current.current?.abort();
|
|
57
|
+
current.current = undefined;
|
|
58
|
+
};
|
|
59
|
+
}, [run]);
|
|
60
|
+
function save() {
|
|
61
|
+
if (!view?.enabled || !editor)
|
|
62
|
+
return;
|
|
63
|
+
const { configId } = view;
|
|
64
|
+
const { field, action } = editor;
|
|
65
|
+
if (action === 'unset')
|
|
66
|
+
void run({ endpoint: 'unset', configId, field });
|
|
67
|
+
else if (editor.value)
|
|
68
|
+
void run({ endpoint: 'set', configId, field, value: editor.value });
|
|
69
|
+
}
|
|
70
|
+
return (_jsxs(Card, { className: "mc-fields", "aria-labelledby": id, children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { id: id, role: "heading", "aria-level": 3, children: t('marivo.credentials.report-publishing-credentials') }), view?.enabled && (_jsxs(CardDescription, { className: "break-words", children: [view.name, " \u00B7 ", view.bucket] })), _jsx(CardAction, { children: _jsx(Button, { variant: "outline", type: "button", disabled: busy, onClick: () => void run({ endpoint: 'describe' }), children: t('marivo.credentials.refresh-credential-status') }) })] }), _jsxs(CardContent, { "aria-busy": busy, children: [error && (_jsx(Alert, { role: "alert", className: "mc-alert", children: _jsx(AlertDescription, { children: t(error) }) })), busy && !view && (_jsx("p", { role: "status", children: t('marivo.credentials.loading-publishing-credentials') })), view?.enabled === false && (_jsx("p", { className: "mc-note", children: t('marivo.credentials.publishing-disabled') })), view?.enabled &&
|
|
71
|
+
view.fields.map((item) => {
|
|
72
|
+
const active = editor?.field === item.field ? editor : undefined;
|
|
73
|
+
const inputId = `${id}-${item.field}`;
|
|
74
|
+
return (_jsx(FieldSet, { className: "mc-field", "aria-label": item.reference, disabled: busy, children: _jsxs(FieldGroup, { children: [_jsxs("div", { className: "mc-field-header", children: [_jsxs("div", { className: "mc-field-title", children: [_jsx("h4", { children: item.reference }), _jsxs("p", { children: [t(labels[item.field]), " ", t('marivo.credentials.source'), item.source ?? t('marivo.credentials.none'), !item.writable && t('marivo.credentials.read-only-source')] })] }), _jsxs("div", { className: "mc-field-actions", children: [_jsx(Badge, { className: "mc-badge", "data-ready": item.configured, children: t(item.configured
|
|
75
|
+
? 'marivo.credentials.configured'
|
|
76
|
+
: 'marivo.credentials.not-configured') }), item.writable && !active && (_jsx(Button, { variant: "outline", type: "button", onClick: () => setEditor({ action: 'set', field: item.field, value: '' }), children: t(item.configured
|
|
77
|
+
? 'marivo.credentials.replace'
|
|
78
|
+
: 'marivo.credentials.add-credential') })), item.writable && item.configured && !active && (_jsx(Button, { variant: "destructive", type: "button", onClick: () => setEditor({ action: 'unset', field: item.field }), children: t('marivo.credentials.delete-saved-value') }))] })] }), active?.action === 'set' && (_jsxs(Field, { children: [_jsx(FieldLabel, { htmlFor: inputId, children: t('marivo.credentials.new-value') }), _jsx(Input, { id: inputId, type: "password", autoComplete: "new-password", maxLength: 65_536, value: active.value, onChange: (event) => setEditor({ ...active, value: event.target.value }) }), _jsxs("div", { className: "mc-field-actions", children: [_jsx(Button, { type: "button", disabled: !active.value, onClick: save, children: t(item.configured
|
|
79
|
+
? 'marivo.credentials.confirm-replacement'
|
|
80
|
+
: 'marivo.credentials.add-credential') }), _jsx(Button, { variant: "outline", type: "button", onClick: () => setEditor(undefined), children: t('marivo.credentials.cancel') })] })] })), active?.action === 'unset' && (_jsxs("div", { className: "mc-confirm", children: [_jsx("p", { children: t('marivo.credentials.delete-shared-publishing-credential', {
|
|
81
|
+
p0: item.reference,
|
|
82
|
+
}) }), _jsxs("div", { className: "mc-field-actions", children: [_jsx(Button, { variant: "destructive", type: "button", onClick: save, children: t('marivo.credentials.confirm-saved-value-deletion') }), _jsx(Button, { variant: "outline", type: "button", onClick: () => setEditor(undefined), children: t('marivo.credentials.keep') })] })] }))] }) }, item.field));
|
|
83
|
+
})] })] }));
|
|
84
|
+
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
export const credentialStyles = `
|
|
3
3
|
.mc-panel{min-width:0;width:100%;container-type:inline-size;--mc-accent:var(--dsw-alias-state-business-primary,#075bea)}
|
|
4
4
|
.mc-shell,.mc-columns,.mc-main,.mc-form{display:flex;flex-direction:column;min-width:0}
|
|
5
|
+
.mc-directory-content{min-width:0}
|
|
6
|
+
.mc-directory-content>.mc-requests{margin-bottom:1rem}
|
|
5
7
|
.mc-shell,.mc-columns,.mc-form-body{gap:1.5rem}
|
|
6
8
|
.mc-form{gap:1rem;flex:1}
|
|
7
9
|
.mc-form-body{display:flex;flex-direction:column;flex:1}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
/** Plugin-owned copy; keys are stable identifiers, never business labels. */
|
|
2
2
|
export const zh = {
|
|
3
|
+
'marivo.credentials.datasources-tab': '数据源',
|
|
4
|
+
'marivo.credentials.credential-management-tab': '凭证管理',
|
|
5
|
+
'marivo.credentials.report-publishing-credentials': '报告发布凭证',
|
|
6
|
+
'marivo.credentials.refresh-credential-status': '刷新凭证状态',
|
|
7
|
+
'marivo.credentials.loading-publishing-credentials': '正在读取发布凭证状态…',
|
|
8
|
+
'marivo.credentials.publishing-disabled': '当前 Host 尚未启用对象存储发布。',
|
|
9
|
+
'marivo.credentials.publishing-config-changed': '发布配置已更新,请刷新状态后重新填写。',
|
|
10
|
+
'marivo.credentials.publishing-credential-unconfirmed': '凭证操作结果未确认,请刷新状态后重试。',
|
|
11
|
+
'marivo.credentials.delete-shared-publishing-credential': '删除 {p0} 的已保存值?当前 Host 中使用此引用的 Workspace 都会受到影响。',
|
|
3
12
|
'marivo.credentials.basic-settings': '基本配置',
|
|
4
13
|
'marivo.credentials.advanced-settings': '高级设置',
|
|
5
14
|
'marivo.credentials.autocommit-required': 'MySQL 和 PostgreSQL 仅支持默认或启用自动提交;请选择默认或 true。',
|
|
@@ -271,6 +280,15 @@ export const zh = {
|
|
|
271
280
|
'marivo.credentials.save-failed': '保存配置失败。',
|
|
272
281
|
};
|
|
273
282
|
export const en = {
|
|
283
|
+
'marivo.credentials.datasources-tab': 'Datasources',
|
|
284
|
+
'marivo.credentials.credential-management-tab': 'Credential management',
|
|
285
|
+
'marivo.credentials.report-publishing-credentials': 'Report publishing credentials',
|
|
286
|
+
'marivo.credentials.refresh-credential-status': 'Refresh credential status',
|
|
287
|
+
'marivo.credentials.loading-publishing-credentials': 'Loading publishing credential status…',
|
|
288
|
+
'marivo.credentials.publishing-disabled': 'Object storage publishing is disabled on this Host.',
|
|
289
|
+
'marivo.credentials.publishing-config-changed': 'Publishing configuration changed. Refresh the status and enter the credential again.',
|
|
290
|
+
'marivo.credentials.publishing-credential-unconfirmed': 'Credential operation unconfirmed. Refresh the status before retrying.',
|
|
291
|
+
'marivo.credentials.delete-shared-publishing-credential': 'Delete the saved value for {p0}? This affects every Workspace using this reference on the current Host.',
|
|
274
292
|
'marivo.credentials.basic-settings': 'Basic configuration',
|
|
275
293
|
'marivo.credentials.advanced-settings': 'Advanced settings',
|
|
276
294
|
'marivo.credentials.autocommit-required': 'MySQL and PostgreSQL only support the default or enabled autocommit; choose default or true.',
|
|
@@ -162,6 +162,18 @@ export const zh = {
|
|
|
162
162
|
'marivo.presentation.export-html-started': 'HTML 下载已开始。',
|
|
163
163
|
'marivo.presentation.export-html-failed': '导出失败,请重试。',
|
|
164
164
|
'marivo.presentation.export-html-dismiss': '关闭导出提示',
|
|
165
|
+
'marivo.presentation.report-delivery-checking': '正在读取导出设置…',
|
|
166
|
+
'marivo.presentation.report-delivery-unavailable': '导出设置不可用,重新打开菜单重试',
|
|
167
|
+
'marivo.presentation.publish-report': '发布到对象存储',
|
|
168
|
+
'marivo.presentation.publishing-report': '正在发布…',
|
|
169
|
+
'marivo.presentation.publish-report-success': '报告已上传到对象存储。',
|
|
170
|
+
'marivo.presentation.publish-report-credentials-missing': '缺少发布凭据。补齐后可重新发布。',
|
|
171
|
+
'marivo.presentation.publish-report-config-changed': '发布配置已变更,请重新打开菜单。',
|
|
172
|
+
'marivo.presentation.publish-report-unconfirmed': '上传结果未确认,请检查对象存储或重试。',
|
|
173
|
+
'marivo.presentation.publish-report-failed': '无法发布报告,请重试。',
|
|
174
|
+
'marivo.presentation.open-published-report': '查看已发布报告',
|
|
175
|
+
'marivo.presentation.open-publishing-credentials': '前往凭证管理',
|
|
176
|
+
'marivo.presentation.publish-report-dismiss': '关闭发布提示',
|
|
165
177
|
'marivo.presentation.ui-open-tab': '在右侧打开分析视图',
|
|
166
178
|
'marivo.presentation.ui-loading': '正在读取已保存快照…',
|
|
167
179
|
'marivo.presentation.ui-preparing': '正在准备分析视图…',
|
|
@@ -398,6 +410,18 @@ export const en = {
|
|
|
398
410
|
'marivo.presentation.export-html-started': 'HTML download started.',
|
|
399
411
|
'marivo.presentation.export-html-failed': 'Export failed. Try again.',
|
|
400
412
|
'marivo.presentation.export-html-dismiss': 'Dismiss export notification',
|
|
413
|
+
'marivo.presentation.report-delivery-checking': 'Checking export settings…',
|
|
414
|
+
'marivo.presentation.report-delivery-unavailable': 'Export settings unavailable. Reopen the menu to retry.',
|
|
415
|
+
'marivo.presentation.publish-report': 'Publish to object storage',
|
|
416
|
+
'marivo.presentation.publishing-report': 'Publishing…',
|
|
417
|
+
'marivo.presentation.publish-report-success': 'Report uploaded to object storage.',
|
|
418
|
+
'marivo.presentation.publish-report-credentials-missing': 'Publishing credentials are missing. Add them and retry.',
|
|
419
|
+
'marivo.presentation.publish-report-config-changed': 'Publishing configuration changed. Reopen the menu.',
|
|
420
|
+
'marivo.presentation.publish-report-unconfirmed': 'Upload unconfirmed. Check object storage or retry.',
|
|
421
|
+
'marivo.presentation.publish-report-failed': 'Could not publish the report. Try again.',
|
|
422
|
+
'marivo.presentation.open-published-report': 'Open published report',
|
|
423
|
+
'marivo.presentation.open-publishing-credentials': 'Go to credential management',
|
|
424
|
+
'marivo.presentation.publish-report-dismiss': 'Dismiss publishing notification',
|
|
401
425
|
'marivo.presentation.ui-open-tab': 'Open analysis view in sidebar',
|
|
402
426
|
'marivo.presentation.ui-loading': 'Reading saved snapshot…',
|
|
403
427
|
'marivo.presentation.ui-preparing': 'Preparing analysis view…',
|
|
@@ -389,7 +389,15 @@ export function installRightTabs(ctx, { diagnostics = false } = {}) {
|
|
|
389
389
|
setCleanupRetrying(false);
|
|
390
390
|
}
|
|
391
391
|
}, children: t('marivo.presentation.delete-report-retry-cleanup') })), cleanupError && _jsx("p", { children: t('marivo.presentation.delete-report-cleanup-failed') })] })) : (_jsxs(_Fragment, { children: [page.target.kind === 'reports' && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "rt-heading-row", children: [_jsx("h2", { className: "rt-heading", children: t(labels[page.target.kind]) }), page.target.kind === 'reports' && (_jsx(Button, { variant: "outline", type: "button", disabled: catalog.loading, onClick: () => void page.refresh(), children: t('marivo.presentation.refresh') }))] }), _jsx("p", { className: "rt-caption", children: workspaces.find((w) => w.workspaceId === page.target.workspaceId)?.title ??
|
|
392
|
-
page.target.workspaceId })] })), state.newer && (_jsx(Alert, { role: "status", className: "rt-notice", children: _jsxs(AlertDescription, { children: [t('marivo.navigation.a-newer-version-exists-the-current-view-is-unchanged'), _jsx(Button, { variant: "outline", type: "button", onClick: () => void page.refresh(), children: t('marivo.navigation.view-new-version') })] }) })), state.notice && _jsx("p", { role: "status", children: t(state.notice) }), page.target.kind === 'reports' && (_jsx(ReportCatalogView, { model: page.catalog, reader: catalogReader, sessions: sessions, onOpenSession: (id) => ctx.uiWorkspace.openSession(id) })), page.target.kind === 'semantic' && (_jsx(SemanticBrowserPanel, { model: page.semantic, onAsk: canAsk ? () => page.semantic.addToQuestion(ctx, page.sessionId) : undefined, workspaces: workspaces })), page.target.kind === 'datasources' && (_jsx(CredentialPanel, { model: page.datasources, workspaces: workspaces,
|
|
392
|
+
page.target.workspaceId })] })), state.newer && (_jsx(Alert, { role: "status", className: "rt-notice", children: _jsxs(AlertDescription, { children: [t('marivo.navigation.a-newer-version-exists-the-current-view-is-unchanged'), _jsx(Button, { variant: "outline", type: "button", onClick: () => void page.refresh(), children: t('marivo.navigation.view-new-version') })] }) })), state.notice && _jsx("p", { role: "status", children: t(state.notice) }), page.target.kind === 'reports' && (_jsx(ReportCatalogView, { model: page.catalog, reader: catalogReader, sessions: sessions, onOpenSession: (id) => ctx.uiWorkspace.openSession(id) })), page.target.kind === 'semantic' && (_jsx(SemanticBrowserPanel, { model: page.semantic, onAsk: canAsk ? () => page.semantic.addToQuestion(ctx, page.sessionId) : undefined, workspaces: workspaces })), page.target.kind === 'datasources' && (_jsx(CredentialPanel, { model: page.datasources, workspaces: workspaces, initialSection: tab.navigation.params?.credentialSection === 'credentials'
|
|
393
|
+
? 'credentials'
|
|
394
|
+
: 'datasources', onRefresh: () => void page.refresh() }, tab.navigation.revision)), page.target.kind === 'report' && (_jsxs(_Fragment, { children: [report.loading && _jsx("p", { role: "status", children: t('marivo.navigation.loading-report') }), report.error && (_jsxs("div", { role: "alert", children: [_jsx("p", { children: t(report.error) }), _jsx(Button, { variant: "outline", type: "button", onClick: () => void page.refresh(), children: t('marivo.navigation.reload-report') })] })), report.historyOpen && (_jsx(ReportHistoryPanel, { state: report, model: historyModel, sessions: sessions, onOpenSession: (id) => ctx.uiWorkspace.openSession(id), onClose: () => void page.reader.toggleHistory() })), report.analysisDocument && (_jsxs("div", { className: "rt-report-surface", children: [!revisionInProgress && (_jsx("div", { className: "rt-toolbar", children: _jsx(ReportActions, { document: report.analysisDocument, rpc: rpc, signal: tab.signal, onOpenPublishingCredentials: () => act(page, () => {
|
|
395
|
+
const { workspaceId } = page.target;
|
|
396
|
+
check(page.sessionId, workspaceId);
|
|
397
|
+
tab.actions.openTab(directoryKind('datasources'), {
|
|
398
|
+
params: { workspaceId, credentialSection: 'credentials' },
|
|
399
|
+
});
|
|
400
|
+
}), onHistory: () => void page.reader.toggleHistory(), onDelete: (isCurrent) => deleteReport(ctx, rpc, page, tab, report.analysisDocument, isCurrent, deleted), refreshLocked: refreshLocks.has(page.sessionId), onRefreshData: (isCurrent) => refreshReport(page, tab, report.analysisDocument, isCurrent), isCurrent: () => !tab.signal.aborted &&
|
|
393
401
|
!page.getSnapshot().error &&
|
|
394
402
|
page.reader.getSnapshot().analysisDocument ===
|
|
395
403
|
report.analysisDocument &&
|
|
@@ -6,11 +6,26 @@ import { useCopy } from "../i18n/context.js";
|
|
|
6
6
|
import { Button, IconButton } from "../ui/controls.js";
|
|
7
7
|
import { useThemedPortal } from "../ui/portal.js";
|
|
8
8
|
import { useExportHtml } from "./export-html.js";
|
|
9
|
-
|
|
9
|
+
import { useReportPublishing } from "./report-publishing.js";
|
|
10
|
+
const deliveryLabels = {
|
|
11
|
+
checking: 'marivo.presentation.report-delivery-checking',
|
|
12
|
+
unavailable: 'marivo.presentation.report-delivery-unavailable',
|
|
13
|
+
download: 'marivo.presentation.export-html',
|
|
14
|
+
publish: 'marivo.presentation.publish-report',
|
|
15
|
+
};
|
|
16
|
+
export function ReportActions({ document, isCurrent, onHistory, refreshLocked, onRefreshData, onDelete, rpc, signal, onOpenPublishingCredentials, }) {
|
|
10
17
|
const t = useCopy();
|
|
11
18
|
const source = useRef(null);
|
|
12
19
|
const container = useThemedPortal(source);
|
|
13
20
|
const { status, download, dismiss } = useExportHtml({ document, isCurrent });
|
|
21
|
+
const publishing = useReportPublishing({ rpc, document, signal, isCurrent });
|
|
22
|
+
const publication = publishing.status;
|
|
23
|
+
const deliveryBusy = status === 'working' || publication?.kind === 'working';
|
|
24
|
+
const deliveryLabel = status === 'working'
|
|
25
|
+
? 'marivo.presentation.exporting-html'
|
|
26
|
+
: publication?.kind === 'working'
|
|
27
|
+
? 'marivo.presentation.publishing-report'
|
|
28
|
+
: deliveryLabels[publishing.mode];
|
|
14
29
|
const [refreshStatus, setRefreshStatus] = useState();
|
|
15
30
|
const pending = useRef(false);
|
|
16
31
|
const [confirmDelete, setConfirmDelete] = useState(false);
|
|
@@ -48,7 +63,18 @@ export function ReportActions({ document, isCurrent, onHistory, refreshLocked, o
|
|
|
48
63
|
pending.current = false;
|
|
49
64
|
}
|
|
50
65
|
};
|
|
51
|
-
return (_jsxs(_Fragment, { children: [_jsxs(DropdownMenu.Root, {
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
return (_jsxs(_Fragment, { children: [_jsxs(DropdownMenu.Root, { onOpenChange: (open) => {
|
|
67
|
+
if (open)
|
|
68
|
+
void publishing.check();
|
|
69
|
+
}, children: [_jsx("span", { ref: source, children: _jsx(DropdownMenu.Trigger, { asChild: true, children: _jsx(IconButton, { label: t('marivo.presentation.report-actions'), variant: "ghost", children: _jsx(Ellipsis, { "aria-hidden": "true" }) }) }) }), _jsx(DropdownMenu.Portal, { container: container, children: _jsxs(DropdownMenu.Content, { align: "end", sideOffset: 4, className: "rt-actions-menu", children: [_jsx(DropdownMenu.Item, { className: "rt-actions-item", disabled: refreshLocked, onSelect: () => void refresh(), children: t('marivo.presentation.refresh-data') }), _jsx(DropdownMenu.Item, { className: "rt-actions-item", onSelect: () => setConfirmDelete(true), children: t('marivo.presentation.delete-report') }), _jsx(DropdownMenu.Item, { className: "rt-actions-item", onSelect: onHistory, children: t('marivo.presentation.history') }), _jsx(DropdownMenu.Item, { className: "rt-actions-item", disabled: deliveryBusy || publishing.mode === 'checking' || publishing.mode === 'unavailable', onSelect: () => {
|
|
70
|
+
if (publishing.mode === 'publish')
|
|
71
|
+
void publishing.publish();
|
|
72
|
+
else if (publishing.mode === 'download')
|
|
73
|
+
void download();
|
|
74
|
+
}, children: t(deliveryLabel) })] }) })] }), _jsx(Dialog.Root, { open: confirmDelete, onOpenChange: (open) => !deleting && setConfirmDelete(open), children: _jsxs(Dialog.Portal, { container: container, children: [_jsx(Dialog.Overlay, { className: "rt-dialog-overlay" }), _jsxs(Dialog.Content, { className: "rt-dialog", children: [_jsx(Dialog.Title, { children: t('marivo.presentation.delete-report-confirm') }), _jsxs(Dialog.Description, { children: [document.title, " \u2014 ", t('marivo.presentation.delete-report-description')] }), _jsxs("div", { className: "rt-dialog-actions", children: [_jsx(Button, { type: "button", variant: "outline", disabled: deleting, onClick: () => setConfirmDelete(false), children: t('marivo.presentation.delete-report-cancel') }), _jsx(Button, { type: "button", variant: "destructive", disabled: deleting, onClick: () => void remove(), children: t('marivo.presentation.delete-report') })] })] })] }) }), deleteStatus && (_jsxs("div", { className: "rt-export-notice rt-export-notice-error", role: "alert", children: [_jsx(CircleAlert, { "aria-hidden": "true" }), _jsx("span", { children: t(`marivo.presentation.delete-report-${deleteStatus}`) }), _jsx(IconButton, { label: t('marivo.presentation.delete-report-dismiss'), variant: "ghost", onClick: () => setDeleteStatus(undefined), children: _jsx(X, { "aria-hidden": "true" }) })] })), refreshStatus && (_jsxs("div", { className: `rt-export-notice${refreshStatus === 'accepted' ? '' : ' rt-export-notice-error'}`, children: [refreshStatus === 'accepted' ? (_jsx(CheckCircle2, { "aria-hidden": "true" })) : (_jsx(CircleAlert, { "aria-hidden": "true" })), _jsx("span", { role: refreshStatus === 'accepted' ? 'status' : 'alert', children: t(`marivo.presentation.refresh-data-${refreshStatus}`) }), _jsx(IconButton, { label: t('marivo.presentation.refresh-data-dismiss'), variant: "ghost", onClick: () => setRefreshStatus(undefined), children: _jsx(X, { "aria-hidden": "true" }) })] })), status === 'success' && (_jsxs("div", { className: "rt-export-notice", role: "status", children: [_jsx(CheckCircle2, { "aria-hidden": "true" }), t('marivo.presentation.export-html-started')] })), status === 'failed' && (_jsxs("div", { className: "rt-export-notice rt-export-notice-error", children: [_jsx(CircleAlert, { "aria-hidden": "true" }), _jsx("span", { role: "alert", children: t('marivo.presentation.export-html-failed') }), _jsx(IconButton, { label: t('marivo.presentation.export-html-dismiss'), variant: "ghost", onClick: dismiss, children: _jsx(X, { "aria-hidden": "true" }) })] })), publication && publication.kind !== 'working' && (_jsxs("div", { className: `rt-export-notice${publication.kind === 'success' ? '' : ' rt-export-notice-error'}`, children: [publication.kind === 'success' ? (_jsx(CheckCircle2, { "aria-hidden": "true" })) : (_jsx(CircleAlert, { "aria-hidden": "true" })), _jsxs("div", { className: "rt-publishing-details", children: [_jsx("span", { role: publication.kind === 'success' ? 'status' : 'alert', children: t(`marivo.presentation.publish-report-${publication.kind}`) }), publication.kind === 'success' && (_jsx("a", { href: publication.url, target: "_blank", rel: "noopener noreferrer", children: t('marivo.presentation.open-published-report') })), publication.kind === 'credentials-missing' && (_jsx(Button, { variant: "link", type: "button", onClick: () => {
|
|
75
|
+
if (!isCurrent())
|
|
76
|
+
return;
|
|
77
|
+
publishing.dismiss();
|
|
78
|
+
onOpenPublishingCredentials();
|
|
79
|
+
}, children: t('marivo.presentation.open-publishing-credentials') }))] }), _jsx(IconButton, { label: t('marivo.presentation.publish-report-dismiss'), variant: "ghost", onClick: publishing.dismiss, children: _jsx(X, { "aria-hidden": "true" }) })] }))] }));
|
|
54
80
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { savedReportRef } from "../../presentation/contracts/report-ref.js";
|
|
4
|
+
import { publishingCredentialResultSchema, REPORT_PUBLISHING_CHANNEL, } from "../../report-publishing/credential-contract.js";
|
|
5
|
+
const publicationResult = z.discriminatedUnion('ok', [
|
|
6
|
+
z.object({
|
|
7
|
+
ok: z.literal(true),
|
|
8
|
+
value: z.object({
|
|
9
|
+
url: z.url({ protocol: /^https?$/ }),
|
|
10
|
+
workspaceId: z.string(),
|
|
11
|
+
reportId: z.string(),
|
|
12
|
+
buildId: z.string(),
|
|
13
|
+
}),
|
|
14
|
+
}),
|
|
15
|
+
z.object({ ok: z.literal(false), error: z.object({ message: z.string() }) }),
|
|
16
|
+
]);
|
|
17
|
+
const failures = {
|
|
18
|
+
'report-publishing-credentials-missing': 'credentials-missing',
|
|
19
|
+
'report-publishing-config-changed': 'config-changed',
|
|
20
|
+
'report-publishing-disabled': 'config-changed',
|
|
21
|
+
'report-publishing-upload-unconfirmed': 'unconfirmed',
|
|
22
|
+
};
|
|
23
|
+
export function useReportPublishing({ rpc, document, signal, isCurrent, }) {
|
|
24
|
+
const [config, setConfig] = useState();
|
|
25
|
+
const [status, setStatus] = useState();
|
|
26
|
+
const active = useRef();
|
|
27
|
+
useEffect(() => () => active.current?.abort(), []);
|
|
28
|
+
const { workspaceId, reportId, buildId } = document;
|
|
29
|
+
async function run(operation) {
|
|
30
|
+
if (active.current || signal.aborted || !isCurrent())
|
|
31
|
+
return;
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
active.current = controller;
|
|
34
|
+
try {
|
|
35
|
+
await operation(AbortSignal.any([signal, controller.signal]));
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
active.current = undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const canUpdate = (operationSignal) => !operationSignal.aborted && isCurrent();
|
|
42
|
+
async function check() {
|
|
43
|
+
await run(async (operationSignal) => {
|
|
44
|
+
setConfig(undefined);
|
|
45
|
+
try {
|
|
46
|
+
const response = publishingCredentialResultSchema.parse(await rpc.call(REPORT_PUBLISHING_CHANNEL, 'describe', { workspaceId }, operationSignal));
|
|
47
|
+
if (canUpdate(operationSignal))
|
|
48
|
+
setConfig(response.ok ? response.value : 'unavailable');
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
if (canUpdate(operationSignal))
|
|
52
|
+
setConfig('unavailable');
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async function publish() {
|
|
57
|
+
if (!config || config === 'unavailable' || !config.enabled)
|
|
58
|
+
return;
|
|
59
|
+
await run(async (operationSignal) => {
|
|
60
|
+
if (config.fields.some((field) => field.required && !field.configured)) {
|
|
61
|
+
setStatus({ kind: 'credentials-missing' });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
setStatus({ kind: 'working' });
|
|
65
|
+
try {
|
|
66
|
+
const response = publicationResult.parse(await rpc.call(REPORT_PUBLISHING_CHANNEL, 'publish', {
|
|
67
|
+
workspaceId,
|
|
68
|
+
configId: config.configId,
|
|
69
|
+
ref: savedReportRef({ workspaceId, reportId, buildId }),
|
|
70
|
+
}, operationSignal));
|
|
71
|
+
if (!canUpdate(operationSignal))
|
|
72
|
+
return;
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const code = response.error.message;
|
|
75
|
+
setStatus({
|
|
76
|
+
kind: Object.hasOwn(failures, code)
|
|
77
|
+
? failures[code]
|
|
78
|
+
: 'failed',
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const receipt = response.value;
|
|
83
|
+
if (receipt.workspaceId !== workspaceId ||
|
|
84
|
+
receipt.reportId !== reportId ||
|
|
85
|
+
receipt.buildId !== buildId)
|
|
86
|
+
throw new Error('publication-owner-mismatch');
|
|
87
|
+
setStatus({ kind: 'success', url: receipt.url });
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
if (canUpdate(operationSignal))
|
|
91
|
+
setStatus({ kind: 'unconfirmed' });
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
if (!operationSignal.aborted)
|
|
95
|
+
setStatus((current) => (current?.kind === 'working' ? undefined : current));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const mode = config === 'unavailable'
|
|
100
|
+
? 'unavailable'
|
|
101
|
+
: config?.enabled
|
|
102
|
+
? 'publish'
|
|
103
|
+
: config
|
|
104
|
+
? 'download'
|
|
105
|
+
: 'checking';
|
|
106
|
+
return { mode, status, check, publish, dismiss: () => setStatus(undefined) };
|
|
107
|
+
}
|
|
@@ -22,6 +22,9 @@ export const rightTabStyles = `
|
|
|
22
22
|
.rt-export-notice>svg{width:1rem;height:1rem;flex:none;color:var(--primary)}
|
|
23
23
|
.rt-export-notice-error>svg{color:var(--destructive)}
|
|
24
24
|
.rt-export-notice button{width:1.75rem;height:1.75rem;flex:none;margin:-.375rem -.375rem -.375rem 0}
|
|
25
|
+
.rt-publishing-details{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;min-width:0;overflow-wrap:anywhere}
|
|
26
|
+
.rt-publishing-details a{color:var(--primary);text-decoration:underline}
|
|
27
|
+
.rt-publishing-details button{width:auto;height:auto;margin:0;padding:0;white-space:normal;text-align:start}
|
|
25
28
|
.rt-notice{display:flex;align-items:center;flex-wrap:wrap;gap:.75rem;margin-bottom:1rem}
|
|
26
29
|
.rt-page .pd-catalog{padding:0;overflow:auto}
|
|
27
30
|
.rt-page .pd-history{position:static;max-height:300px;margin-bottom:1rem;width:100%}
|
|
@@ -686,6 +686,9 @@ export const COMPILED_UI_STYLES = `/*! tailwindcss v4.3.4 | MIT License | https:
|
|
|
686
686
|
:where(.mv-ui, .mv-ui *).text-balance {
|
|
687
687
|
text-wrap: balance;
|
|
688
688
|
}
|
|
689
|
+
:where(.mv-ui, .mv-ui *).break-words {
|
|
690
|
+
overflow-wrap: break-word;
|
|
691
|
+
}
|
|
689
692
|
:where(.mv-ui, .mv-ui *).whitespace-normal {
|
|
690
693
|
white-space: normal;
|
|
691
694
|
}
|