@skillcdn/console 0.4.1 → 0.4.3
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 +19 -2
- package/SKILLCDN.md +7 -5
- package/dist/api.d.ts +5 -4
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +5 -4
- package/dist/api.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +569 -48
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +34 -3
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +283 -7
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +44 -7
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/plugin-check.d.ts +26 -0
- package/dist/cli/plugin-check.d.ts.map +1 -0
- package/dist/cli/plugin-check.js +396 -0
- package/dist/cli/plugin-check.js.map +1 -0
- package/dist/cli/plugin-cli.d.ts +20 -0
- package/dist/cli/plugin-cli.d.ts.map +1 -0
- package/dist/cli/plugin-cli.js +189 -0
- package/dist/cli/plugin-cli.js.map +1 -0
- package/dist/cli/plugin-init.d.ts +37 -0
- package/dist/cli/plugin-init.d.ts.map +1 -0
- package/dist/cli/plugin-init.js +206 -0
- package/dist/cli/plugin-init.js.map +1 -0
- package/dist/cli/plugin-main.d.ts +3 -0
- package/dist/cli/plugin-main.d.ts.map +1 -0
- package/dist/cli/plugin-main.js +28 -0
- package/dist/cli/plugin-main.js.map +1 -0
- package/dist/client.d.ts +100 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +79 -7
- package/dist/client.js.map +1 -1
- package/dist/compat.d.ts +1 -1
- package/dist/compat.js +2 -2
- package/dist/components/board.d.ts +8 -0
- package/dist/components/board.d.ts.map +1 -1
- package/dist/components/board.js +59 -15
- package/dist/components/board.js.map +1 -1
- package/dist/components/comments.d.ts.map +1 -1
- package/dist/components/comments.js +1 -1
- package/dist/components/comments.js.map +1 -1
- package/dist/components/decision-form.d.ts +2 -1
- package/dist/components/decision-form.d.ts.map +1 -1
- package/dist/components/decision-form.js +36 -4
- package/dist/components/decision-form.js.map +1 -1
- package/dist/components/decision-list.d.ts +4 -0
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +29 -3
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/event-feed.d.ts +1 -1
- package/dist/components/event-feed.d.ts.map +1 -1
- package/dist/components/event-feed.js +33 -1
- package/dist/components/event-feed.js.map +1 -1
- package/dist/components/my-decisions.d.ts +2 -0
- package/dist/components/my-decisions.d.ts.map +1 -1
- package/dist/components/my-decisions.js +2 -2
- package/dist/components/my-decisions.js.map +1 -1
- package/dist/components/plugin-frame.d.ts +52 -0
- package/dist/components/plugin-frame.d.ts.map +1 -0
- package/dist/components/plugin-frame.js +314 -0
- package/dist/components/plugin-frame.js.map +1 -0
- package/dist/components/plugin-settings.d.ts +38 -0
- package/dist/components/plugin-settings.d.ts.map +1 -0
- package/dist/components/plugin-settings.js +348 -0
- package/dist/components/plugin-settings.js.map +1 -0
- package/dist/components/shell.d.ts +2 -0
- package/dist/components/shell.d.ts.map +1 -1
- package/dist/components/shell.js +26 -1
- package/dist/components/shell.js.map +1 -1
- package/dist/components/task-view.d.ts +2 -1
- package/dist/components/task-view.d.ts.map +1 -1
- package/dist/components/task-view.js.map +1 -1
- package/dist/components/workspace.d.ts +7 -1
- package/dist/components/workspace.d.ts.map +1 -1
- package/dist/components/workspace.js +1 -1
- package/dist/components/workspace.js.map +1 -1
- package/dist/console.css +330 -1
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +152 -41
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +9 -2
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +45 -9
- package/dist/data.js.map +1 -1
- package/dist/i18n/en.d.ts +155 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +166 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +150 -0
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +2 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +2 -0
- package/dist/limits.js.map +1 -1
- package/dist/plugin/args.d.ts +148 -0
- package/dist/plugin/args.d.ts.map +1 -0
- package/dist/plugin/args.js +369 -0
- package/dist/plugin/args.js.map +1 -0
- package/dist/plugin/bridge.d.ts +146 -0
- package/dist/plugin/bridge.d.ts.map +1 -0
- package/dist/plugin/bridge.js +125 -0
- package/dist/plugin/bridge.js.map +1 -0
- package/dist/plugin/capabilities.d.ts +20 -0
- package/dist/plugin/capabilities.d.ts.map +1 -0
- package/dist/plugin/capabilities.js +133 -0
- package/dist/plugin/capabilities.js.map +1 -0
- package/dist/plugin/context.d.ts +380 -0
- package/dist/plugin/context.d.ts.map +1 -0
- package/dist/plugin/context.js +2 -0
- package/dist/plugin/context.js.map +1 -0
- package/dist/plugin/define.d.ts +208 -0
- package/dist/plugin/define.d.ts.map +1 -0
- package/dist/plugin/define.js +482 -0
- package/dist/plugin/define.js.map +1 -0
- package/dist/plugin/definition-error.d.ts +11 -0
- package/dist/plugin/definition-error.d.ts.map +1 -0
- package/dist/plugin/definition-error.js +17 -0
- package/dist/plugin/definition-error.js.map +1 -0
- package/dist/plugin/limits.d.ts +67 -0
- package/dist/plugin/limits.d.ts.map +1 -0
- package/dist/plugin/limits.js +70 -0
- package/dist/plugin/limits.js.map +1 -0
- package/dist/plugin/load-worker.d.ts +7 -0
- package/dist/plugin/load-worker.d.ts.map +1 -0
- package/dist/plugin/load-worker.js +41 -0
- package/dist/plugin/load-worker.js.map +1 -0
- package/dist/plugin/load.d.ts +18 -0
- package/dist/plugin/load.d.ts.map +1 -0
- package/dist/plugin/load.js +62 -0
- package/dist/plugin/load.js.map +1 -0
- package/dist/plugin/manifest.d.ts +237 -0
- package/dist/plugin/manifest.d.ts.map +1 -0
- package/dist/plugin/manifest.js +411 -0
- package/dist/plugin/manifest.js.map +1 -0
- package/dist/plugin/refusals.d.ts +49 -0
- package/dist/plugin/refusals.d.ts.map +1 -0
- package/dist/plugin/refusals.js +79 -0
- package/dist/plugin/refusals.js.map +1 -0
- package/dist/plugin/rest-schemas.d.ts +556 -0
- package/dist/plugin/rest-schemas.d.ts.map +1 -0
- package/dist/plugin/rest-schemas.js +262 -0
- package/dist/plugin/rest-schemas.js.map +1 -0
- package/dist/plugin/sandbox.d.ts +23 -0
- package/dist/plugin/sandbox.d.ts.map +1 -0
- package/dist/plugin/sandbox.js +99 -0
- package/dist/plugin/sandbox.js.map +1 -0
- package/dist/plugin/testing.d.ts +45 -0
- package/dist/plugin/testing.d.ts.map +1 -0
- package/dist/plugin/testing.js +420 -0
- package/dist/plugin/testing.js.map +1 -0
- package/dist/plugin/web-react.d.ts +11 -0
- package/dist/plugin/web-react.d.ts.map +1 -0
- package/dist/plugin/web-react.js +34 -0
- package/dist/plugin/web-react.js.map +1 -0
- package/dist/plugin/web.d.ts +95 -0
- package/dist/plugin/web.d.ts.map +1 -0
- package/dist/plugin/web.js +245 -0
- package/dist/plugin/web.js.map +1 -0
- package/dist/plugin-nav.d.ts +29 -0
- package/dist/plugin-nav.d.ts.map +1 -0
- package/dist/plugin-nav.js +92 -0
- package/dist/plugin-nav.js.map +1 -0
- package/dist/plugin-testing.d.ts +2 -0
- package/dist/plugin-testing.d.ts.map +1 -0
- package/dist/plugin-testing.js +4 -0
- package/dist/plugin-testing.js.map +1 -0
- package/dist/plugin-web-react.d.ts +3 -0
- package/dist/plugin-web-react.d.ts.map +1 -0
- package/dist/plugin-web-react.js +3 -0
- package/dist/plugin-web-react.js.map +1 -0
- package/dist/plugin-web.d.ts +4 -0
- package/dist/plugin-web.d.ts.map +1 -0
- package/dist/plugin-web.js +5 -0
- package/dist/plugin-web.js.map +1 -0
- package/dist/plugin.d.ts +11 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +15 -0
- package/dist/plugin.js.map +1 -0
- package/dist/router.d.ts +28 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +47 -0
- package/dist/router.js.map +1 -1
- package/dist/routes.d.ts +4 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +6 -0
- package/dist/routes.js.map +1 -1
- package/dist/schemas.d.ts +194 -9
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +98 -17
- package/dist/schemas.js.map +1 -1
- package/dist/tokens.d.ts +2 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +101 -0
- package/dist/tokens.js.map +1 -0
- package/dist/vocabulary.d.ts +11 -1
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +24 -0
- package/dist/vocabulary.js.map +1 -1
- package/dist/web.d.ts.map +1 -1
- package/dist/web.js +2 -0
- package/dist/web.js.map +1 -1
- package/docs/specs/cli.md +12 -5
- package/docs/specs/plugins.md +488 -0
- package/docs/specs/rest.md +11 -9
- package/package.json +26 -8
- package/skills/building-a-console/SKILL.md +25 -26
- package/skills/building-a-console/references/configuring.md +40 -0
- package/skills/building-a-console/references/the-default-console.md +1 -1
- package/skills/building-a-console/references/the-look-from-a-website.md +5 -5
- package/skills/building-a-plugin/SKILL.md +137 -0
- package/skills/deploying-a-console/SKILL.md +24 -26
- package/skills/deploying-a-console/references/access.md +13 -9
- package/skills/deploying-a-console/references/resources.md +25 -10
- package/skills/deploying-a-console/references/running-it-again.md +15 -0
- package/skills/deploying-a-console/references/sizing.md +2 -0
- package/skills/planning-the-board/SKILL.md +96 -0
- package/skills/planning-the-board/references/a-task.md +36 -0
- package/skills/planning-the-board/references/setting-up.md +44 -0
- package/skills/working-the-board/SKILL.md +43 -34
- package/skills/working-the-board/references/the-pack.md +19 -0
- package/src/api.ts +12 -0
- package/src/cli/cli.ts +737 -55
- package/src/cli/format.ts +368 -10
- package/src/cli/help.ts +50 -6
- package/src/cli/plugin-check.ts +497 -0
- package/src/cli/plugin-cli.ts +237 -0
- package/src/cli/plugin-init.ts +257 -0
- package/src/cli/plugin-main.ts +30 -0
- package/src/client.ts +322 -9
- package/src/compat.ts +2 -2
- package/src/components/board.tsx +62 -14
- package/src/components/comments.tsx +1 -0
- package/src/components/decision-form.tsx +92 -11
- package/src/components/decision-list.tsx +71 -2
- package/src/components/event-feed.tsx +42 -1
- package/src/components/my-decisions.tsx +4 -1
- package/src/components/plugin-frame.tsx +437 -0
- package/src/components/plugin-settings.tsx +1111 -0
- package/src/components/shell.tsx +82 -14
- package/src/components/task-view.tsx +2 -1
- package/src/components/workspace.tsx +18 -0
- package/src/console.tsx +304 -31
- package/src/data.ts +59 -11
- package/src/i18n/en.ts +175 -0
- package/src/i18n/ko.ts +155 -0
- package/src/index.ts +12 -0
- package/src/limits.ts +2 -0
- package/src/plugin/args.ts +526 -0
- package/src/plugin/bridge.ts +314 -0
- package/src/plugin/capabilities.ts +150 -0
- package/src/plugin/context.ts +421 -0
- package/src/plugin/define.ts +792 -0
- package/src/plugin/definition-error.ts +19 -0
- package/src/plugin/limits.ts +70 -0
- package/src/plugin/load-worker.ts +50 -0
- package/src/plugin/load.ts +82 -0
- package/src/plugin/manifest.ts +561 -0
- package/src/plugin/refusals.ts +94 -0
- package/src/plugin/rest-schemas.ts +354 -0
- package/src/plugin/sandbox.ts +123 -0
- package/src/plugin/testing.ts +612 -0
- package/src/plugin/web-react.ts +45 -0
- package/src/plugin/web.ts +403 -0
- package/src/plugin-nav.ts +132 -0
- package/src/plugin-testing.ts +10 -0
- package/src/plugin-web-react.ts +4 -0
- package/src/plugin-web.ts +35 -0
- package/src/plugin.ts +126 -0
- package/src/router.ts +73 -0
- package/src/routes.ts +7 -0
- package/src/schemas.ts +115 -18
- package/src/styles/console.css +330 -1
- package/src/tokens.ts +101 -0
- package/src/vocabulary.ts +30 -0
- package/src/web.ts +2 -0
- package/template/plugin/.gitignore +2 -0
- package/template/plugin/README.md +26 -0
- package/template/plugin/SKILLCDN.md +13 -0
- package/template/plugin/build.mjs +40 -0
- package/template/plugin/console-plugin.json +50 -0
- package/template/plugin/package.json +27 -0
- package/template/plugin/skills/__key__/SKILL.md +23 -0
- package/template/plugin/src/check.test.ts +34 -0
- package/template/plugin/src/server/index.test.ts +41 -0
- package/template/plugin/src/server/index.ts +72 -0
- package/template/plugin/src/web/index.css +51 -0
- package/template/plugin/src/web/index.ts +115 -0
- package/template/plugin/tsconfig.json +17 -0
- package/skills/working-the-board/references/setting-up.md +0 -3
- /package/skills/{working-the-board → planning-the-board}/references/project-rules.md +0 -0
package/src/console.tsx
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
type RestDecisionPatch,
|
|
24
24
|
type RestEvent,
|
|
25
25
|
type RestMyDecisions,
|
|
26
|
+
type RestPluginDetail,
|
|
26
27
|
type RestProject,
|
|
27
28
|
type RestProjectInput,
|
|
28
29
|
type RestProjectPatch,
|
|
@@ -50,6 +51,8 @@ import { MyDecisions } from "./components/my-decisions.js";
|
|
|
50
51
|
import { PackTokenWays } from "./components/pack-token.js";
|
|
51
52
|
import { PAGE_SIZE } from "./components/paged.js";
|
|
52
53
|
import { ListingForm, PeopleList, type PeopleListProps } from "./components/people.js";
|
|
54
|
+
import { PluginFrame, PluginWidgets } from "./components/plugin-frame.js";
|
|
55
|
+
import { PluginSettingsList, PluginSettingsView } from "./components/plugin-settings.js";
|
|
53
56
|
import {
|
|
54
57
|
MemberList,
|
|
55
58
|
type MemberListProps,
|
|
@@ -104,6 +107,7 @@ import {
|
|
|
104
107
|
type Messages,
|
|
105
108
|
useWords,
|
|
106
109
|
} from "./i18n/index.js";
|
|
110
|
+
import { pluginNavItems, pluginPagesAt, pluginWordOf } from "./plugin-nav.js";
|
|
107
111
|
import {
|
|
108
112
|
boardHref,
|
|
109
113
|
calendarHref,
|
|
@@ -121,6 +125,8 @@ import {
|
|
|
121
125
|
PATHS,
|
|
122
126
|
personAgentsHref,
|
|
123
127
|
personWorkHref,
|
|
128
|
+
pluginHref,
|
|
129
|
+
pluginSettingsHref,
|
|
124
130
|
projectHref,
|
|
125
131
|
projectSettingsHref,
|
|
126
132
|
type Route,
|
|
@@ -350,6 +356,14 @@ function useFollowLinks(navigation: Navigation): void {
|
|
|
350
356
|
}
|
|
351
357
|
|
|
352
358
|
/** The key of the project a route is on, or nothing. */
|
|
359
|
+
/** The files chosen or pasted on a page, as the client sends them: each by its name, its bytes and its type where the browser knows one. */
|
|
360
|
+
const uploadsOf = (files: readonly File[]): FileUpload[] =>
|
|
361
|
+
files.map((file) => ({
|
|
362
|
+
name: file.name,
|
|
363
|
+
bytes: file,
|
|
364
|
+
...(file.type === "" ? {} : { contentType: file.type }),
|
|
365
|
+
}));
|
|
366
|
+
|
|
353
367
|
const projectOf = (route: Route): string | undefined =>
|
|
354
368
|
"project" in route ? route.project : undefined;
|
|
355
369
|
|
|
@@ -393,11 +407,27 @@ function ProjectsPage(props: {
|
|
|
393
407
|
readonly data: ConsoleData;
|
|
394
408
|
readonly components: ConsoleComponents;
|
|
395
409
|
readonly navigation: Navigation;
|
|
410
|
+
readonly client: ConsoleClient;
|
|
411
|
+
readonly language: string;
|
|
396
412
|
/** Whether the form for a new project is open: the page is at its address. */
|
|
397
413
|
readonly making: boolean;
|
|
398
414
|
}) {
|
|
399
415
|
const words = useWords().projects;
|
|
400
|
-
const { data, components, navigation } = props;
|
|
416
|
+
const { data, components, navigation, client, language } = props;
|
|
417
|
+
const me = data.me;
|
|
418
|
+
/** The plugins' widgets at a place of this page (ADR-0054), for the person's roles. */
|
|
419
|
+
const widgets = (place: "home:top" | "home:bottom") =>
|
|
420
|
+
me === undefined ? null : (
|
|
421
|
+
<PluginWidgets
|
|
422
|
+
plugins={data.plugins}
|
|
423
|
+
place={place}
|
|
424
|
+
me={me}
|
|
425
|
+
language={language}
|
|
426
|
+
client={client}
|
|
427
|
+
navigation={navigation}
|
|
428
|
+
nameOf={(name) => pluginWordOf(name, language)}
|
|
429
|
+
/>
|
|
430
|
+
);
|
|
401
431
|
const { busy, error, act } = useAction();
|
|
402
432
|
const href = (project: RestProject) => projectHref(project.key);
|
|
403
433
|
// A project is made by an administrator (ADR-0045): a member sees the list alone, the form's
|
|
@@ -415,6 +445,7 @@ function ProjectsPage(props: {
|
|
|
415
445
|
)}
|
|
416
446
|
</div>
|
|
417
447
|
{error !== undefined && <Callout tone="danger">{error}</Callout>}
|
|
448
|
+
{widgets("home:top")}
|
|
418
449
|
{making && (
|
|
419
450
|
<section className="sc-panel sc-panel-form" aria-label={words.form.aria}>
|
|
420
451
|
<ProjectForm
|
|
@@ -450,10 +481,150 @@ function ProjectsPage(props: {
|
|
|
450
481
|
/>
|
|
451
482
|
}
|
|
452
483
|
/>
|
|
484
|
+
{widgets("home:bottom")}
|
|
485
|
+
</>
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* A plugin's page (ADR-0054): the sub-tabs of its pages at the level, where it has several, above
|
|
491
|
+
* the frame of the one shown, the first when the address names none. Nothing for a plugin the
|
|
492
|
+
* person has no page of, or that is not on the project.
|
|
493
|
+
*/
|
|
494
|
+
function PluginPage(props: {
|
|
495
|
+
readonly route: Extract<Route, { name: "plugin" }>;
|
|
496
|
+
readonly data: ConsoleData;
|
|
497
|
+
readonly project: RestProject | undefined;
|
|
498
|
+
readonly client: ConsoleClient;
|
|
499
|
+
readonly language: string;
|
|
500
|
+
readonly navigation: Navigation;
|
|
501
|
+
}) {
|
|
502
|
+
const words = useWords();
|
|
503
|
+
const { route, data, project, client, language, navigation } = props;
|
|
504
|
+
const plugin = data.plugins.find(
|
|
505
|
+
(candidate) => candidate.key === route.plugin && candidate.state === "enabled",
|
|
506
|
+
);
|
|
507
|
+
const bound =
|
|
508
|
+
route.level === "workspace" ||
|
|
509
|
+
data.projectPlugins.some((candidate) => candidate.key === route.plugin);
|
|
510
|
+
const pages = plugin === undefined ? [] : pluginPagesAt(plugin, route.level);
|
|
511
|
+
const page = route.page === undefined ? pages[0] : pages.find((p) => p.key === route.page);
|
|
512
|
+
if (plugin === undefined || !bound || page === undefined || data.me === undefined) {
|
|
513
|
+
return <EmptyState title={words.plugins.noPage.title} body={words.plugins.noPage.body} />;
|
|
514
|
+
}
|
|
515
|
+
const projectKey = route.level === "project" ? route.project : undefined;
|
|
516
|
+
return (
|
|
517
|
+
<>
|
|
518
|
+
{pages.length > 1 && (
|
|
519
|
+
<nav className="sc-subtabs" aria-label={pluginWordOf(plugin.name, language)}>
|
|
520
|
+
{pages.map((candidate) => (
|
|
521
|
+
<a
|
|
522
|
+
key={candidate.key}
|
|
523
|
+
href={pluginHref(plugin.key, candidate.key, projectKey)}
|
|
524
|
+
className={candidate.key === page.key ? "sc-tab sc-tab-current" : "sc-tab"}
|
|
525
|
+
aria-current={candidate.key === page.key ? "page" : undefined}
|
|
526
|
+
>
|
|
527
|
+
{pluginWordOf(candidate.name, language)}
|
|
528
|
+
</a>
|
|
529
|
+
))}
|
|
530
|
+
</nav>
|
|
531
|
+
)}
|
|
532
|
+
<PluginFrame
|
|
533
|
+
key={`${plugin.key}:${page.key}:${projectKey ?? ""}`}
|
|
534
|
+
plugin={plugin}
|
|
535
|
+
page={page.key}
|
|
536
|
+
project={project}
|
|
537
|
+
path={route.rest}
|
|
538
|
+
me={data.me}
|
|
539
|
+
language={language}
|
|
540
|
+
client={client}
|
|
541
|
+
navigation={navigation}
|
|
542
|
+
events={project === undefined ? undefined : data.events}
|
|
543
|
+
title={words.plugins.frame(pluginWordOf(page.name, language))}
|
|
544
|
+
/>
|
|
453
545
|
</>
|
|
454
546
|
);
|
|
455
547
|
}
|
|
456
548
|
|
|
549
|
+
/**
|
|
550
|
+
* The plugins' settings (docs/specs/plugins.md, "The settings pages"): the list, or one plugin
|
|
551
|
+
* whole, read again after every change; for an administrator.
|
|
552
|
+
*/
|
|
553
|
+
function PluginSettingsPage(props: {
|
|
554
|
+
readonly plugin: string | undefined;
|
|
555
|
+
readonly data: ConsoleData;
|
|
556
|
+
readonly client: ConsoleClient;
|
|
557
|
+
readonly language: string;
|
|
558
|
+
readonly navigation: Navigation;
|
|
559
|
+
}) {
|
|
560
|
+
const words = useWords();
|
|
561
|
+
const { data, client, language, navigation } = props;
|
|
562
|
+
const settings = words.plugins.settings;
|
|
563
|
+
const administrator = data.me?.person?.role === "admin";
|
|
564
|
+
const [detail, setDetail] = useState<RestPluginDetail | undefined>(undefined);
|
|
565
|
+
const [failure, setFailure] = useState<string | undefined>(undefined);
|
|
566
|
+
const key = props.plugin;
|
|
567
|
+
const read = useCallback(async () => {
|
|
568
|
+
if (key === undefined) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
try {
|
|
572
|
+
setDetail(await client.plugin(key).get());
|
|
573
|
+
setFailure(undefined);
|
|
574
|
+
} catch (error) {
|
|
575
|
+
setFailure(error instanceof ApiError ? error.message : words.common.somethingWentWrong);
|
|
576
|
+
}
|
|
577
|
+
}, [client, key, words.common.somethingWentWrong]);
|
|
578
|
+
useEffect(() => {
|
|
579
|
+
setDetail(undefined);
|
|
580
|
+
void read();
|
|
581
|
+
}, [read]);
|
|
582
|
+
if (!administrator) {
|
|
583
|
+
return <NothingHere />;
|
|
584
|
+
}
|
|
585
|
+
if (key === undefined) {
|
|
586
|
+
return (
|
|
587
|
+
<>
|
|
588
|
+
<div className="sc-page-head">
|
|
589
|
+
<h1 className="sc-page-title">{settings.title}</h1>
|
|
590
|
+
</div>
|
|
591
|
+
<PluginSettingsList
|
|
592
|
+
plugins={data.plugins}
|
|
593
|
+
language={language}
|
|
594
|
+
href={(plugin) => pluginSettingsHref(plugin.key)}
|
|
595
|
+
/>
|
|
596
|
+
</>
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
if (failure !== undefined) {
|
|
600
|
+
return <EmptyState title={words.common.somethingWentWrong} body={failure} />;
|
|
601
|
+
}
|
|
602
|
+
if (detail === undefined) {
|
|
603
|
+
return (
|
|
604
|
+
<div className="sc-loading">
|
|
605
|
+
<Spinner label={words.common.loading} />
|
|
606
|
+
</div>
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
return (
|
|
610
|
+
<PluginSettingsView
|
|
611
|
+
detail={detail}
|
|
612
|
+
projects={data.projects}
|
|
613
|
+
people={data.people}
|
|
614
|
+
language={language}
|
|
615
|
+
client={client}
|
|
616
|
+
onChanged={async () => {
|
|
617
|
+
await read();
|
|
618
|
+
await data.actions.refreshPlugins();
|
|
619
|
+
}}
|
|
620
|
+
onPurged={() => {
|
|
621
|
+
void data.actions.refreshPlugins();
|
|
622
|
+
navigation.replace(pluginSettingsHref());
|
|
623
|
+
}}
|
|
624
|
+
/>
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
|
|
457
628
|
/**
|
|
458
629
|
* The archive of the board (ADR-0029): the tasks put away, newest first, a page at a time from
|
|
459
630
|
* the server, each brought back from here by whoever may work in the project.
|
|
@@ -700,9 +871,9 @@ function TaskPage(props: {
|
|
|
700
871
|
})
|
|
701
872
|
}
|
|
702
873
|
onMove={(target, state) => void act(() => data.actions.updateTask(target.id, { state }))}
|
|
703
|
-
onRaiseDecision={(input) =>
|
|
874
|
+
onRaiseDecision={(input, files) =>
|
|
704
875
|
void act(async () => {
|
|
705
|
-
await data.actions.raiseDecision(input);
|
|
876
|
+
await data.actions.raiseDecision(input, uploadsOf(files));
|
|
706
877
|
navigation.replace(here);
|
|
707
878
|
})
|
|
708
879
|
}
|
|
@@ -726,14 +897,7 @@ function TaskPage(props: {
|
|
|
726
897
|
}
|
|
727
898
|
onComment={(draft) =>
|
|
728
899
|
void act(async () => {
|
|
729
|
-
await data.actions.comment(task.id, {
|
|
730
|
-
body: draft.body,
|
|
731
|
-
files: draft.files.map((file) => ({
|
|
732
|
-
name: file.name,
|
|
733
|
-
bytes: file,
|
|
734
|
-
...(file.type === "" ? {} : { contentType: file.type }),
|
|
735
|
-
})),
|
|
736
|
-
});
|
|
900
|
+
await data.actions.comment(task.id, { body: draft.body, files: uploadsOf(draft.files) });
|
|
737
901
|
setComments(await taskComments(task.id));
|
|
738
902
|
})
|
|
739
903
|
}
|
|
@@ -747,6 +911,8 @@ function ProjectPage(props: {
|
|
|
747
911
|
readonly data: ConsoleData;
|
|
748
912
|
readonly components: ConsoleComponents;
|
|
749
913
|
readonly navigation: Navigation;
|
|
914
|
+
readonly client: ConsoleClient;
|
|
915
|
+
readonly language: string;
|
|
750
916
|
}) {
|
|
751
917
|
const words = useWords();
|
|
752
918
|
const { route, project, data, components, navigation } = props;
|
|
@@ -1146,6 +1312,7 @@ function ProjectPage(props: {
|
|
|
1146
1312
|
tasks={tasksById}
|
|
1147
1313
|
taskHref={href}
|
|
1148
1314
|
docHref={toDoc}
|
|
1315
|
+
fileHref={(fileId) => projectPath(key, "files", fileId)}
|
|
1149
1316
|
decisionHref={(decision) => decisionHref(key, decision.id)}
|
|
1150
1317
|
page={route.page}
|
|
1151
1318
|
pageHref={(page) => decisionsHref(key, page)}
|
|
@@ -1172,9 +1339,9 @@ function ProjectPage(props: {
|
|
|
1172
1339
|
deciders={data.members.map((member) => member.person)}
|
|
1173
1340
|
me={data.me?.person ?? undefined}
|
|
1174
1341
|
busy={busy}
|
|
1175
|
-
onSubmit={(input: RestDecisionInput) =>
|
|
1342
|
+
onSubmit={(input: RestDecisionInput, files: readonly File[]) =>
|
|
1176
1343
|
void act(async () => {
|
|
1177
|
-
const made = await data.actions.raiseDecision(input);
|
|
1344
|
+
const made = await data.actions.raiseDecision(input, uploadsOf(files));
|
|
1178
1345
|
navigation.replace(decisionHref(key, made.id));
|
|
1179
1346
|
})
|
|
1180
1347
|
}
|
|
@@ -1201,6 +1368,7 @@ function ProjectPage(props: {
|
|
|
1201
1368
|
task={decision.taskId === null ? undefined : tasksById.get(decision.taskId)}
|
|
1202
1369
|
taskHref={href}
|
|
1203
1370
|
docHref={toDoc}
|
|
1371
|
+
fileHref={(fileId) => projectPath(key, "files", fileId)}
|
|
1204
1372
|
onAnswer={onAnswer}
|
|
1205
1373
|
onUpdate={onUpdate}
|
|
1206
1374
|
busy={busy}
|
|
@@ -1313,6 +1481,18 @@ function ProjectPage(props: {
|
|
|
1313
1481
|
<ProjectSettingsPage route={route} project={project} data={data} navigation={navigation} />
|
|
1314
1482
|
);
|
|
1315
1483
|
}
|
|
1484
|
+
if (route.name === "plugin") {
|
|
1485
|
+
return (
|
|
1486
|
+
<PluginPage
|
|
1487
|
+
route={route}
|
|
1488
|
+
data={data}
|
|
1489
|
+
project={project}
|
|
1490
|
+
client={props.client}
|
|
1491
|
+
language={props.language}
|
|
1492
|
+
navigation={navigation}
|
|
1493
|
+
/>
|
|
1494
|
+
);
|
|
1495
|
+
}
|
|
1316
1496
|
return <NothingHere />;
|
|
1317
1497
|
}
|
|
1318
1498
|
|
|
@@ -1578,9 +1758,10 @@ function WorkspaceSettingsPage(props: {
|
|
|
1578
1758
|
readonly form: "rules" | "workspace" | "pack-token" | undefined;
|
|
1579
1759
|
readonly data: ConsoleData;
|
|
1580
1760
|
readonly navigation: Navigation;
|
|
1761
|
+
readonly language: string;
|
|
1581
1762
|
}) {
|
|
1582
1763
|
const words = useWords();
|
|
1583
|
-
const { form, data, navigation } = props;
|
|
1764
|
+
const { form, data, navigation, language } = props;
|
|
1584
1765
|
const { busy, error, act } = useAction();
|
|
1585
1766
|
const administrator = data.me?.person?.role === "admin";
|
|
1586
1767
|
const workspace = data.me?.workspace;
|
|
@@ -1729,7 +1910,7 @@ function WorkspaceSettingsPage(props: {
|
|
|
1729
1910
|
</a>
|
|
1730
1911
|
)}
|
|
1731
1912
|
</div>
|
|
1732
|
-
<WorkspaceSettingsView workspace={workspace} />
|
|
1913
|
+
<WorkspaceSettingsView workspace={workspace} console={data.me?.console} />
|
|
1733
1914
|
</section>
|
|
1734
1915
|
)}
|
|
1735
1916
|
<section className="sc-panel" aria-label={words.rules.title}>
|
|
@@ -1753,6 +1934,20 @@ function WorkspaceSettingsPage(props: {
|
|
|
1753
1934
|
/>
|
|
1754
1935
|
)}
|
|
1755
1936
|
</section>
|
|
1937
|
+
{administrator && (
|
|
1938
|
+
<section className="sc-panel" aria-label={words.plugins.settings.title}>
|
|
1939
|
+
<h2 className="sc-section-title">{words.plugins.settings.title}</h2>
|
|
1940
|
+
{data.plugins.length === 0 ? (
|
|
1941
|
+
<p className="sc-settings-hint">{words.plugins.settings.empty.body}</p>
|
|
1942
|
+
) : (
|
|
1943
|
+
<PluginSettingsList
|
|
1944
|
+
plugins={data.plugins}
|
|
1945
|
+
language={language}
|
|
1946
|
+
href={(plugin) => pluginSettingsHref(plugin.key)}
|
|
1947
|
+
/>
|
|
1948
|
+
)}
|
|
1949
|
+
</section>
|
|
1950
|
+
)}
|
|
1756
1951
|
<section className="sc-panel" aria-label={words.settings.categories}>
|
|
1757
1952
|
<h2 className="sc-section-title">{words.settings.categories}</h2>
|
|
1758
1953
|
{data.categories.length > 0 && (
|
|
@@ -2109,6 +2304,7 @@ function MyDecisionsPage(props: { readonly data: ConsoleData }) {
|
|
|
2109
2304
|
addressed={{ items: addressed.items, more: addressed.more, onMore: addressed.readMore }}
|
|
2110
2305
|
raised={{ items: raised.items, more: raised.more, onMore: raised.readMore }}
|
|
2111
2306
|
decisionHref={decisionHref}
|
|
2307
|
+
fileHref={(projectKey, fileId) => projectPath(projectKey, "files", fileId)}
|
|
2112
2308
|
onAnswer={onAnswer}
|
|
2113
2309
|
busy={busy || addressed.busy || raised.busy}
|
|
2114
2310
|
/>
|
|
@@ -2174,11 +2370,36 @@ function WorkspacePage(props: {
|
|
|
2174
2370
|
readonly data: ConsoleData;
|
|
2175
2371
|
readonly components: ConsoleComponents;
|
|
2176
2372
|
readonly navigation: Navigation;
|
|
2373
|
+
readonly client: ConsoleClient;
|
|
2374
|
+
readonly language: string;
|
|
2177
2375
|
}) {
|
|
2178
2376
|
const words = useWords();
|
|
2179
|
-
const { route, data, components, navigation } = props;
|
|
2377
|
+
const { route, data, components, navigation, client, language } = props;
|
|
2180
2378
|
const [fresh, setFresh] = useState<RestTokenCreated | undefined>(undefined);
|
|
2181
2379
|
const { busy, error, act } = useAction();
|
|
2380
|
+
if (route.name === "plugin" && route.level === "workspace") {
|
|
2381
|
+
return (
|
|
2382
|
+
<PluginPage
|
|
2383
|
+
route={route}
|
|
2384
|
+
data={data}
|
|
2385
|
+
project={undefined}
|
|
2386
|
+
client={client}
|
|
2387
|
+
language={language}
|
|
2388
|
+
navigation={navigation}
|
|
2389
|
+
/>
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2392
|
+
if (route.name === "plugins-settings") {
|
|
2393
|
+
return (
|
|
2394
|
+
<PluginSettingsPage
|
|
2395
|
+
plugin={route.plugin}
|
|
2396
|
+
data={data}
|
|
2397
|
+
client={client}
|
|
2398
|
+
language={language}
|
|
2399
|
+
navigation={navigation}
|
|
2400
|
+
/>
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2182
2403
|
|
|
2183
2404
|
if (route.name === "work") {
|
|
2184
2405
|
return <WorkPage data={data} components={components} week={route.week} />;
|
|
@@ -2190,7 +2411,14 @@ function WorkspacePage(props: {
|
|
|
2190
2411
|
return <PersonWorkPage id={route.id} data={data} components={components} week={route.week} />;
|
|
2191
2412
|
}
|
|
2192
2413
|
if (route.name === "workspace-settings") {
|
|
2193
|
-
return
|
|
2414
|
+
return (
|
|
2415
|
+
<WorkspaceSettingsPage
|
|
2416
|
+
form={route.form}
|
|
2417
|
+
data={data}
|
|
2418
|
+
navigation={navigation}
|
|
2419
|
+
language={language}
|
|
2420
|
+
/>
|
|
2421
|
+
);
|
|
2194
2422
|
}
|
|
2195
2423
|
if (route.name === "categories") {
|
|
2196
2424
|
const me = data.me?.person ?? undefined;
|
|
@@ -2351,6 +2579,7 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
2351
2579
|
const languages = config.languages ?? DEFAULT_LANGUAGES;
|
|
2352
2580
|
const pages = config.pages ?? [];
|
|
2353
2581
|
|
|
2582
|
+
// biome-ignore lint/correctness/noComponentHookFactories: createConsole runs once, when a console is built, never during a render; App is that console's one root component, closing over its config.
|
|
2354
2583
|
function App(props: { readonly initialPath?: string | undefined }) {
|
|
2355
2584
|
const { location, navigation } = useNavigation(props.initialPath);
|
|
2356
2585
|
useFollowLinks(navigation);
|
|
@@ -2435,6 +2664,7 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
2435
2664
|
href: PATHS.decisions,
|
|
2436
2665
|
label: words.nav.myDecisions,
|
|
2437
2666
|
current: route.name === "my-decisions",
|
|
2667
|
+
count: data.me?.decisions?.waiting,
|
|
2438
2668
|
},
|
|
2439
2669
|
{
|
|
2440
2670
|
href: PATHS.people,
|
|
@@ -2453,13 +2683,6 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
2453
2683
|
current: route.name === "agents" || route.name === "new-token",
|
|
2454
2684
|
},
|
|
2455
2685
|
]),
|
|
2456
|
-
// The settings are read by everyone: the rules every agent follows, the categories and
|
|
2457
|
-
// what the colours mean, where the skills come from; kept by an administrator.
|
|
2458
|
-
{
|
|
2459
|
-
href: PATHS.settings,
|
|
2460
|
-
label: words.nav.settings,
|
|
2461
|
-
current: route.name === "workspace-settings" || route.name === "categories",
|
|
2462
|
-
},
|
|
2463
2686
|
...pages
|
|
2464
2687
|
.filter((candidate) => candidate.level === "workspace")
|
|
2465
2688
|
.map((candidate) => ({
|
|
@@ -2470,6 +2693,28 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
2470
2693
|
route.level === "workspace" &&
|
|
2471
2694
|
route.page === candidate.key,
|
|
2472
2695
|
})),
|
|
2696
|
+
// The plugins' tabs (ADR-0054): the groups that hold a plugin the person has a page of.
|
|
2697
|
+
...pluginNavItems({
|
|
2698
|
+
plugins: data.plugins,
|
|
2699
|
+
level: "workspace",
|
|
2700
|
+
current: {
|
|
2701
|
+
plugin:
|
|
2702
|
+
route.name === "plugin" && route.level === "workspace" ? route.plugin : undefined,
|
|
2703
|
+
},
|
|
2704
|
+
words,
|
|
2705
|
+
language: pack.tag,
|
|
2706
|
+
}),
|
|
2707
|
+
// The settings are read by everyone: the rules every agent follows, the categories and
|
|
2708
|
+
// what the colours mean, where the skills come from, the plugins; kept by an
|
|
2709
|
+
// administrator. Last, after the organization's own tabs and the plugins'.
|
|
2710
|
+
{
|
|
2711
|
+
href: PATHS.settings,
|
|
2712
|
+
label: words.nav.settings,
|
|
2713
|
+
current:
|
|
2714
|
+
route.name === "workspace-settings" ||
|
|
2715
|
+
route.name === "categories" ||
|
|
2716
|
+
route.name === "plugins-settings",
|
|
2717
|
+
},
|
|
2473
2718
|
];
|
|
2474
2719
|
const nav: NavItem[] =
|
|
2475
2720
|
projectKey === undefined
|
|
@@ -2521,12 +2766,6 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
2521
2766
|
label: words.nav.members,
|
|
2522
2767
|
current: route.name === "members",
|
|
2523
2768
|
},
|
|
2524
|
-
// Read by every member: what the project is, and what its agents are asked (ADR-0023).
|
|
2525
|
-
{
|
|
2526
|
-
href: projectHref(projectKey, "settings"),
|
|
2527
|
-
label: words.nav.settings,
|
|
2528
|
-
current: route.name === "settings",
|
|
2529
|
-
},
|
|
2530
2769
|
...pages
|
|
2531
2770
|
.filter((candidate) => candidate.level === "project")
|
|
2532
2771
|
.map((candidate) => ({
|
|
@@ -2537,6 +2776,24 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
2537
2776
|
route.level === "project" &&
|
|
2538
2777
|
route.page === candidate.key,
|
|
2539
2778
|
})),
|
|
2779
|
+
...pluginNavItems({
|
|
2780
|
+
plugins: data.plugins,
|
|
2781
|
+
level: "project",
|
|
2782
|
+
project: { key: projectKey, plugins: data.projectPlugins },
|
|
2783
|
+
current: {
|
|
2784
|
+
plugin:
|
|
2785
|
+
route.name === "plugin" && route.level === "project" ? route.plugin : undefined,
|
|
2786
|
+
},
|
|
2787
|
+
words,
|
|
2788
|
+
language: pack.tag,
|
|
2789
|
+
}),
|
|
2790
|
+
// Read by every member: what the project is, and what its agents are asked
|
|
2791
|
+
// (ADR-0023). Last, after the organization's own tabs and the plugins'.
|
|
2792
|
+
{
|
|
2793
|
+
href: projectHref(projectKey, "settings"),
|
|
2794
|
+
label: words.nav.settings,
|
|
2795
|
+
current: route.name === "settings",
|
|
2796
|
+
},
|
|
2540
2797
|
];
|
|
2541
2798
|
/** A page of the console's own at the address, or nothing there. */
|
|
2542
2799
|
const customPageOf = (key: string, level: CustomPage["level"]): ReactNode => {
|
|
@@ -2562,6 +2819,8 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
2562
2819
|
data={data}
|
|
2563
2820
|
components={components}
|
|
2564
2821
|
navigation={navigation}
|
|
2822
|
+
client={client}
|
|
2823
|
+
language={pack.tag}
|
|
2565
2824
|
making={route.name === "new-project"}
|
|
2566
2825
|
/>
|
|
2567
2826
|
);
|
|
@@ -2574,6 +2833,8 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
2574
2833
|
data={data}
|
|
2575
2834
|
components={components}
|
|
2576
2835
|
navigation={navigation}
|
|
2836
|
+
client={client}
|
|
2837
|
+
language={pack.tag}
|
|
2577
2838
|
/>
|
|
2578
2839
|
);
|
|
2579
2840
|
} else if (data.projectError !== undefined) {
|
|
@@ -2600,6 +2861,8 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
2600
2861
|
data={data}
|
|
2601
2862
|
components={components}
|
|
2602
2863
|
navigation={navigation}
|
|
2864
|
+
client={client}
|
|
2865
|
+
language={pack.tag}
|
|
2603
2866
|
/>
|
|
2604
2867
|
);
|
|
2605
2868
|
}
|
|
@@ -2632,7 +2895,17 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
2632
2895
|
route.name === "calendar" ||
|
|
2633
2896
|
(route.name === "goal" && route.form === undefined) ||
|
|
2634
2897
|
(route.name === "custom" &&
|
|
2635
|
-
pages.some((candidate) => candidate.key === route.page && candidate.wide === true))
|
|
2898
|
+
pages.some((candidate) => candidate.key === route.page && candidate.wide === true)) ||
|
|
2899
|
+
(route.name === "plugin" &&
|
|
2900
|
+
data.plugins.some(
|
|
2901
|
+
(candidate) =>
|
|
2902
|
+
candidate.key === route.plugin &&
|
|
2903
|
+
pluginPagesAt(candidate, route.level).some(
|
|
2904
|
+
(candidate, index) =>
|
|
2905
|
+
candidate.wide &&
|
|
2906
|
+
(route.page === undefined ? index === 0 : candidate.key === route.page),
|
|
2907
|
+
),
|
|
2908
|
+
))
|
|
2636
2909
|
}
|
|
2637
2910
|
onNavigate={(href) => navigation.go(href)}
|
|
2638
2911
|
onSignOut={viaToken ? undefined : () => void data.actions.signOut()}
|