@skillcdn/console 0.4.3 → 0.4.5

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.
Files changed (268) hide show
  1. package/README.md +6 -6
  2. package/dist/api.d.ts +4 -4
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +4 -4
  5. package/dist/api.js.map +1 -1
  6. package/dist/cli/cli.d.ts.map +1 -1
  7. package/dist/cli/cli.js +255 -33
  8. package/dist/cli/cli.js.map +1 -1
  9. package/dist/cli/format.d.ts +20 -5
  10. package/dist/cli/format.d.ts.map +1 -1
  11. package/dist/cli/format.js +98 -26
  12. package/dist/cli/format.js.map +1 -1
  13. package/dist/cli/help.d.ts.map +1 -1
  14. package/dist/cli/help.js +39 -15
  15. package/dist/cli/help.js.map +1 -1
  16. package/dist/cli/plugin-check.d.ts +5 -1
  17. package/dist/cli/plugin-check.d.ts.map +1 -1
  18. package/dist/cli/plugin-check.js +23 -4
  19. package/dist/cli/plugin-check.js.map +1 -1
  20. package/dist/cli/plugin-cli.d.ts.map +1 -1
  21. package/dist/cli/plugin-cli.js +10 -1
  22. package/dist/cli/plugin-cli.js.map +1 -1
  23. package/dist/cli/plugin-init.d.ts +2 -0
  24. package/dist/cli/plugin-init.d.ts.map +1 -1
  25. package/dist/cli/plugin-init.js +6 -0
  26. package/dist/cli/plugin-init.js.map +1 -1
  27. package/dist/client.d.ts +32 -1
  28. package/dist/client.d.ts.map +1 -1
  29. package/dist/client.js +14 -2
  30. package/dist/client.js.map +1 -1
  31. package/dist/compat.js +1 -1
  32. package/dist/components/board.d.ts +8 -0
  33. package/dist/components/board.d.ts.map +1 -1
  34. package/dist/components/board.js +49 -18
  35. package/dist/components/board.js.map +1 -1
  36. package/dist/components/decision-form.d.ts +6 -3
  37. package/dist/components/decision-form.d.ts.map +1 -1
  38. package/dist/components/decision-form.js +24 -18
  39. package/dist/components/decision-form.js.map +1 -1
  40. package/dist/components/decision-list.d.ts +5 -1
  41. package/dist/components/decision-list.d.ts.map +1 -1
  42. package/dist/components/decision-list.js +25 -5
  43. package/dist/components/decision-list.js.map +1 -1
  44. package/dist/components/event-feed.d.ts.map +1 -1
  45. package/dist/components/event-feed.js +34 -8
  46. package/dist/components/event-feed.js.map +1 -1
  47. package/dist/components/goals.d.ts.map +1 -1
  48. package/dist/components/goals.js +2 -2
  49. package/dist/components/goals.js.map +1 -1
  50. package/dist/components/home.d.ts +35 -0
  51. package/dist/components/home.d.ts.map +1 -0
  52. package/dist/components/home.js +326 -0
  53. package/dist/components/home.js.map +1 -0
  54. package/dist/components/my-decisions.d.ts +20 -3
  55. package/dist/components/my-decisions.d.ts.map +1 -1
  56. package/dist/components/my-decisions.js +14 -1
  57. package/dist/components/my-decisions.js.map +1 -1
  58. package/dist/components/pack-connect.d.ts.map +1 -1
  59. package/dist/components/pack-connect.js +5 -4
  60. package/dist/components/pack-connect.js.map +1 -1
  61. package/dist/components/people.d.ts +45 -1
  62. package/dist/components/people.d.ts.map +1 -1
  63. package/dist/components/people.js +125 -4
  64. package/dist/components/people.js.map +1 -1
  65. package/dist/components/picker.d.ts +8 -0
  66. package/dist/components/picker.d.ts.map +1 -1
  67. package/dist/components/picker.js +2 -2
  68. package/dist/components/picker.js.map +1 -1
  69. package/dist/components/plugin-frame.d.ts +3 -13
  70. package/dist/components/plugin-frame.d.ts.map +1 -1
  71. package/dist/components/plugin-frame.js +75 -21
  72. package/dist/components/plugin-frame.js.map +1 -1
  73. package/dist/components/plugin-settings.d.ts +5 -1
  74. package/dist/components/plugin-settings.d.ts.map +1 -1
  75. package/dist/components/plugin-settings.js +283 -45
  76. package/dist/components/plugin-settings.js.map +1 -1
  77. package/dist/components/projects.d.ts +0 -8
  78. package/dist/components/projects.d.ts.map +1 -1
  79. package/dist/components/projects.js +4 -14
  80. package/dist/components/projects.js.map +1 -1
  81. package/dist/components/searches.d.ts +11 -0
  82. package/dist/components/searches.d.ts.map +1 -0
  83. package/dist/components/searches.js +30 -0
  84. package/dist/components/searches.js.map +1 -0
  85. package/dist/components/task-form.d.ts.map +1 -1
  86. package/dist/components/task-form.js +6 -5
  87. package/dist/components/task-form.js.map +1 -1
  88. package/dist/components/task-view.d.ts.map +1 -1
  89. package/dist/components/task-view.js +2 -1
  90. package/dist/components/task-view.js.map +1 -1
  91. package/dist/components/ui.d.ts +16 -0
  92. package/dist/components/ui.d.ts.map +1 -1
  93. package/dist/components/ui.js +15 -2
  94. package/dist/components/ui.js.map +1 -1
  95. package/dist/connect-page.d.ts.map +1 -1
  96. package/dist/connect-page.js +5 -6
  97. package/dist/connect-page.js.map +1 -1
  98. package/dist/console.css +1123 -258
  99. package/dist/console.d.ts +12 -4
  100. package/dist/console.d.ts.map +1 -1
  101. package/dist/console.js +123 -27
  102. package/dist/console.js.map +1 -1
  103. package/dist/data.d.ts +18 -3
  104. package/dist/data.d.ts.map +1 -1
  105. package/dist/data.js +41 -1
  106. package/dist/data.js.map +1 -1
  107. package/dist/docs-page.d.ts.map +1 -1
  108. package/dist/docs-page.js +6 -5
  109. package/dist/docs-page.js.map +1 -1
  110. package/dist/home-tiles.d.ts +92 -0
  111. package/dist/home-tiles.d.ts.map +1 -0
  112. package/dist/home-tiles.js +294 -0
  113. package/dist/home-tiles.js.map +1 -0
  114. package/dist/i18n/en.d.ts +264 -1
  115. package/dist/i18n/en.d.ts.map +1 -1
  116. package/dist/i18n/en.js +260 -1
  117. package/dist/i18n/en.js.map +1 -1
  118. package/dist/i18n/index.d.ts +4 -2
  119. package/dist/i18n/index.d.ts.map +1 -1
  120. package/dist/i18n/index.js +10 -11
  121. package/dist/i18n/index.js.map +1 -1
  122. package/dist/i18n/ko.d.ts.map +1 -1
  123. package/dist/i18n/ko.js +255 -4
  124. package/dist/i18n/ko.js.map +1 -1
  125. package/dist/index.d.ts +7 -5
  126. package/dist/index.d.ts.map +1 -1
  127. package/dist/index.js +7 -5
  128. package/dist/index.js.map +1 -1
  129. package/dist/limits.d.ts +12 -0
  130. package/dist/limits.d.ts.map +1 -1
  131. package/dist/limits.js +12 -0
  132. package/dist/limits.js.map +1 -1
  133. package/dist/plugin/args.d.ts +6 -1
  134. package/dist/plugin/args.d.ts.map +1 -1
  135. package/dist/plugin/args.js +16 -11
  136. package/dist/plugin/args.js.map +1 -1
  137. package/dist/plugin/bridge.d.ts +63 -3
  138. package/dist/plugin/bridge.d.ts.map +1 -1
  139. package/dist/plugin/bridge.js +54 -0
  140. package/dist/plugin/bridge.js.map +1 -1
  141. package/dist/plugin/capabilities.d.ts +7 -1
  142. package/dist/plugin/capabilities.d.ts.map +1 -1
  143. package/dist/plugin/capabilities.js +24 -1
  144. package/dist/plugin/capabilities.js.map +1 -1
  145. package/dist/plugin/context.d.ts +174 -12
  146. package/dist/plugin/context.d.ts.map +1 -1
  147. package/dist/plugin/define.d.ts +14 -1
  148. package/dist/plugin/define.d.ts.map +1 -1
  149. package/dist/plugin/define.js +18 -2
  150. package/dist/plugin/define.js.map +1 -1
  151. package/dist/plugin/limits.d.ts +9 -2
  152. package/dist/plugin/limits.d.ts.map +1 -1
  153. package/dist/plugin/limits.js +9 -2
  154. package/dist/plugin/limits.js.map +1 -1
  155. package/dist/plugin/manifest.d.ts +98 -6
  156. package/dist/plugin/manifest.d.ts.map +1 -1
  157. package/dist/plugin/manifest.js +171 -10
  158. package/dist/plugin/manifest.js.map +1 -1
  159. package/dist/plugin/rest-schemas.d.ts +120 -0
  160. package/dist/plugin/rest-schemas.d.ts.map +1 -1
  161. package/dist/plugin/rest-schemas.js +78 -5
  162. package/dist/plugin/rest-schemas.js.map +1 -1
  163. package/dist/plugin/sandbox.d.ts +4 -3
  164. package/dist/plugin/sandbox.d.ts.map +1 -1
  165. package/dist/plugin/sandbox.js +33 -6
  166. package/dist/plugin/sandbox.js.map +1 -1
  167. package/dist/plugin/semver.d.ts +26 -0
  168. package/dist/plugin/semver.d.ts.map +1 -0
  169. package/dist/plugin/semver.js +76 -0
  170. package/dist/plugin/semver.js.map +1 -0
  171. package/dist/plugin/testing.d.ts +15 -1
  172. package/dist/plugin/testing.d.ts.map +1 -1
  173. package/dist/plugin/testing.js +42 -5
  174. package/dist/plugin/testing.js.map +1 -1
  175. package/dist/plugin/web.d.ts +16 -1
  176. package/dist/plugin/web.d.ts.map +1 -1
  177. package/dist/plugin/web.js +44 -1
  178. package/dist/plugin/web.js.map +1 -1
  179. package/dist/plugin.d.ts +3 -2
  180. package/dist/plugin.d.ts.map +1 -1
  181. package/dist/plugin.js +3 -2
  182. package/dist/plugin.js.map +1 -1
  183. package/dist/router.d.ts +19 -0
  184. package/dist/router.d.ts.map +1 -1
  185. package/dist/router.js +18 -0
  186. package/dist/router.js.map +1 -1
  187. package/dist/routes.d.ts +11 -2
  188. package/dist/routes.d.ts.map +1 -1
  189. package/dist/routes.js +14 -1
  190. package/dist/routes.js.map +1 -1
  191. package/dist/rules.d.ts +2 -2
  192. package/dist/rules.js +3 -3
  193. package/dist/schemas.d.ts +864 -2
  194. package/dist/schemas.d.ts.map +1 -1
  195. package/dist/schemas.js +156 -9
  196. package/dist/schemas.js.map +1 -1
  197. package/dist/use-action.d.ts +8 -2
  198. package/dist/use-action.d.ts.map +1 -1
  199. package/dist/use-action.js +13 -6
  200. package/dist/use-action.js.map +1 -1
  201. package/dist/vocabulary.d.ts +3 -1
  202. package/dist/vocabulary.d.ts.map +1 -1
  203. package/dist/vocabulary.js +9 -0
  204. package/dist/vocabulary.js.map +1 -1
  205. package/docs/specs/cli.md +10 -6
  206. package/docs/specs/plugins.md +83 -62
  207. package/docs/specs/rest.md +34 -16
  208. package/package.json +1 -1
  209. package/skills/building-a-plugin/SKILL.md +15 -12
  210. package/skills/planning-the-board/references/setting-up.md +2 -1
  211. package/skills/working-the-board/SKILL.md +3 -3
  212. package/src/api.ts +23 -0
  213. package/src/cli/cli.ts +323 -30
  214. package/src/cli/format.ts +122 -26
  215. package/src/cli/help.ts +43 -14
  216. package/src/cli/plugin-check.ts +41 -2
  217. package/src/cli/plugin-cli.ts +10 -1
  218. package/src/cli/plugin-init.ts +8 -0
  219. package/src/client.ts +78 -0
  220. package/src/compat.ts +1 -1
  221. package/src/components/board.tsx +86 -45
  222. package/src/components/decision-form.tsx +73 -36
  223. package/src/components/decision-list.tsx +45 -13
  224. package/src/components/event-feed.tsx +40 -7
  225. package/src/components/goals.tsx +4 -1
  226. package/src/components/home.tsx +615 -0
  227. package/src/components/my-decisions.tsx +50 -5
  228. package/src/components/pack-connect.tsx +5 -4
  229. package/src/components/people.tsx +500 -61
  230. package/src/components/picker.tsx +11 -2
  231. package/src/components/plugin-frame.tsx +85 -63
  232. package/src/components/plugin-settings.tsx +735 -185
  233. package/src/components/projects.tsx +14 -69
  234. package/src/components/searches.ts +47 -0
  235. package/src/components/task-form.tsx +23 -28
  236. package/src/components/task-view.tsx +7 -0
  237. package/src/components/ui.tsx +42 -2
  238. package/src/connect-page.tsx +5 -6
  239. package/src/console.tsx +303 -64
  240. package/src/data.ts +62 -3
  241. package/src/docs-page.tsx +6 -5
  242. package/src/home-tiles.ts +438 -0
  243. package/src/i18n/en.ts +274 -0
  244. package/src/i18n/index.ts +17 -14
  245. package/src/i18n/ko.ts +267 -4
  246. package/src/index.ts +18 -5
  247. package/src/limits.ts +12 -0
  248. package/src/plugin/args.ts +16 -11
  249. package/src/plugin/bridge.ts +122 -1
  250. package/src/plugin/capabilities.ts +28 -1
  251. package/src/plugin/context.ts +186 -9
  252. package/src/plugin/define.ts +38 -1
  253. package/src/plugin/limits.ts +9 -2
  254. package/src/plugin/manifest.ts +228 -13
  255. package/src/plugin/rest-schemas.ts +89 -6
  256. package/src/plugin/sandbox.ts +36 -8
  257. package/src/plugin/semver.ts +89 -0
  258. package/src/plugin/testing.ts +63 -6
  259. package/src/plugin/web.ts +66 -0
  260. package/src/plugin.ts +19 -0
  261. package/src/router.ts +25 -0
  262. package/src/routes.ts +17 -2
  263. package/src/rules.ts +3 -3
  264. package/src/schemas.ts +197 -10
  265. package/src/styles/console.css +1123 -258
  266. package/src/use-action.ts +16 -7
  267. package/src/vocabulary.ts +10 -0
  268. package/template/plugin/console-plugin.json +3 -1
package/dist/console.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import { type ComponentType, type ReactNode } from "react";
2
- import { type ConsoleClient, type RestProject } from "./api.js";
2
+ import { type ConsoleClient, type RestMe, type RestProject } from "./api.js";
3
3
  import { type BoardProps } from "./components/board.js";
4
4
  import { type CalendarProps } from "./components/calendar.js";
5
5
  import { type CategoryListProps } from "./components/categories.js";
6
6
  import { type DecisionListProps } from "./components/decision-list.js";
7
7
  import { type EventFeedProps } from "./components/event-feed.js";
8
8
  import { type GoalListProps } from "./components/goals.js";
9
- import { type PeopleListProps } from "./components/people.js";
10
- import { type MemberListProps, type ProjectListProps } from "./components/projects.js";
9
+ import { type HomeGridProps } from "./components/home.js";
10
+ import { type PeopleListProps, type TeamListProps } from "./components/people.js";
11
+ import { type MemberListProps } from "./components/projects.js";
11
12
  import { type Brand, type ShellProps } from "./components/shell.js";
12
13
  import { type SignInProps } from "./components/sign-in.js";
13
14
  import { type SkillListProps } from "./components/skills.js";
@@ -27,8 +28,9 @@ export interface ConsoleComponents {
27
28
  readonly SignIn: ComponentType<SignInProps>;
28
29
  readonly TokenList: ComponentType<TokenListProps>;
29
30
  readonly PeopleList: ComponentType<PeopleListProps>;
31
+ readonly TeamList: ComponentType<TeamListProps>;
30
32
  readonly SkillList: ComponentType<SkillListProps>;
31
- readonly ProjectList: ComponentType<ProjectListProps>;
33
+ readonly HomeGrid: ComponentType<HomeGridProps>;
32
34
  readonly MemberList: ComponentType<MemberListProps>;
33
35
  readonly Calendar: ComponentType<CalendarProps>;
34
36
  readonly GoalList: ComponentType<GoalListProps>;
@@ -91,6 +93,12 @@ export interface ConsoleApp {
91
93
  /** Renders the console into `container`. Returns the way to take it down again. */
92
94
  mount(container: Element): () => void;
93
95
  }
96
+ /**
97
+ * The language to keep for a person the pages see for the first time (ADR-0065): the one their
98
+ * browser asks for, so that a plugin or an agent can write for them while they are away. Nothing
99
+ * for a person who has a language, for nobody, and where the pages only fall back on a language.
100
+ */
101
+ export declare function languageToKeep(me: RestMe | undefined, browser: readonly string[], packs: readonly LanguagePack[]): string | undefined;
94
102
  /**
95
103
  * The pages a console that holds a token does not offer: the tokens themselves, and the
96
104
  * approval of an agent's connection, which makes one; a person's own doing on the console's
@@ -1 +1 @@
1
- {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../src/console.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../src/console.tsx"],"names":[],"mappings":"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"}
package/dist/console.js CHANGED
@@ -10,13 +10,14 @@ import { DecisionForm } from "./components/decision-form.js";
10
10
  import { DecisionCard, DecisionList } from "./components/decision-list.js";
11
11
  import { EventFeed } from "./components/event-feed.js";
12
12
  import { GoalForm, GoalList, GoalView, goalMonth } from "./components/goals.js";
13
- import { MyDecisions } from "./components/my-decisions.js";
13
+ import { HomeGrid } from "./components/home.js";
14
+ import { MyDecisions, ReachedDecision } from "./components/my-decisions.js";
14
15
  import { PackTokenWays } from "./components/pack-token.js";
15
16
  import { PAGE_SIZE } from "./components/paged.js";
16
- import { ListingForm, PeopleList } from "./components/people.js";
17
- import { PluginFrame, PluginWidgets } from "./components/plugin-frame.js";
17
+ import { ListingForm, PeopleList, TeamForm, TeamList, } from "./components/people.js";
18
+ import { PluginFrame } from "./components/plugin-frame.js";
18
19
  import { PluginSettingsList, PluginSettingsView } from "./components/plugin-settings.js";
19
- import { MemberList, ProjectForm, ProjectList, ProjectMark, } from "./components/projects.js";
20
+ import { MemberList, ProjectForm, ProjectMark, } from "./components/projects.js";
20
21
  import { RemotePicturesContext } from "./components/remote-pictures.js";
21
22
  import { GoalTimeline } from "./components/roadmap.js";
22
23
  import { RulesForm, RulesView } from "./components/rules.js";
@@ -27,16 +28,16 @@ import { TaskForm } from "./components/task-form.js";
27
28
  import { TaskList } from "./components/task-list.js";
28
29
  import { TaskView } from "./components/task-view.js";
29
30
  import { NewToken, TokenForm, TokenList } from "./components/tokens.js";
30
- import { Button, Callout, CategoryBadge, EmptyState, LinkButton, personName, Spinner, StateBadge, Time, } from "./components/ui.js";
31
+ import { Button, Callout, CategoryBadge, cx, EmptyState, LinkButton, personName, Spinner, StateBadge, Time, } from "./components/ui.js";
31
32
  import { WorkList } from "./components/work.js";
32
33
  import { peopleVisibilityOf, WorkspaceSettingsForm, WorkspaceSettingsView, weekStartOf, } from "./components/workspace.js";
33
34
  import { ConnectPage } from "./connect-page.js";
34
35
  import { useConsoleData } from "./data.js";
35
36
  import { monthOf, today } from "./dates.js";
36
37
  import { DocsPage } from "./docs-page.js";
37
- import { chooseLanguage, DEFAULT_LANGUAGES, LanguageContext, useWords, } from "./i18n/index.js";
38
+ import { browserPack, chooseLanguage, DEFAULT_LANGUAGES, LanguageContext, useWords, } from "./i18n/index.js";
38
39
  import { pluginNavItems, pluginPagesAt, pluginWordOf } from "./plugin-nav.js";
39
- import { boardHref, calendarHref, customPageHref, decisionHref, decisionsHref, docHref, goalHref, matchRoute, newDecisionHref, newGoalHref, newTaskHref, PATHS, personAgentsHref, personWorkHref, pluginHref, pluginSettingsHref, projectHref, projectSettingsHref, roadmapHref, settingsHref, signInFailureOf, taskHref, withoutSignInParam, workHref, } from "./router.js";
40
+ import { boardHref, calendarHref, customPageHref, decisionHref, decisionsHref, docHref, goalHref, matchRoute, myDecisionHref, newDecisionHref, newGoalHref, newTaskHref, PATHS, personAgentsHref, personWorkHref, pluginHref, pluginSettingsHref, projectHref, projectSettingsHref, roadmapHref, settingsHref, signInFailureOf, taskHref, withoutSignInParam, workHref, } from "./router.js";
40
41
  import { projectPath } from "./routes.js";
41
42
  import { errorWords, useAction } from "./use-action.js";
42
43
  export const DEFAULT_COMPONENTS = {
@@ -48,8 +49,9 @@ export const DEFAULT_COMPONENTS = {
48
49
  SignIn,
49
50
  TokenList,
50
51
  PeopleList,
52
+ TeamList,
51
53
  SkillList,
52
- ProjectList,
54
+ HomeGrid,
53
55
  MemberList,
54
56
  Calendar,
55
57
  GoalList,
@@ -184,6 +186,16 @@ function Ground(props) {
184
186
  function holdsToken(data) {
185
187
  return data.me !== undefined && data.me.agent !== null;
186
188
  }
189
+ /**
190
+ * The language to keep for a person the pages see for the first time (ADR-0065): the one their
191
+ * browser asks for, so that a plugin or an agent can write for them while they are away. Nothing
192
+ * for a person who has a language, for nobody, and where the pages only fall back on a language.
193
+ */
194
+ export function languageToKeep(me, browser, packs) {
195
+ return me?.person !== null && me?.person !== undefined && me.language === null
196
+ ? browserPack(browser, packs)?.tag
197
+ : undefined;
198
+ }
187
199
  /**
188
200
  * The pages a console that holds a token does not offer: the tokens themselves, and the
189
201
  * approval of an agent's connection, which makes one; a person's own doing on the console's
@@ -201,20 +213,21 @@ function NotWithToken() {
201
213
  }
202
214
  function ProjectsPage(props) {
203
215
  const words = useWords().projects;
216
+ const homeWords = useWords().home;
204
217
  const { data, components, navigation, client, language } = props;
205
218
  const me = data.me;
206
- /** The plugins' widgets at a place of this page (ADR-0054), for the person's roles. */
207
- const widgets = (place) => me === undefined ? null : (_jsx(PluginWidgets, { plugins: data.plugins, place: place, me: me, language: language, client: client, navigation: navigation, nameOf: (name) => pluginWordOf(name, language) }));
219
+ // The person arranges the page (ADR-0061): the projects and the widgets, tiles on one grid.
220
+ const [arranging, setArranging] = useState(false);
208
221
  const { busy, error, act } = useAction();
209
222
  const href = (project) => projectHref(project.key);
210
223
  // A project is made by an administrator (ADR-0045): a member sees the list alone, the form's
211
224
  // address included.
212
225
  const administrator = data.me?.person?.role === "admin";
213
226
  const making = props.making && administrator;
214
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sc-page-head", children: [_jsx("h1", { className: "sc-page-title", children: words.title }), administrator && !making && (_jsx(LinkButton, { href: PATHS.newProject, variant: "primary", children: words.newProject }))] }), error !== undefined && _jsx(Callout, { tone: "danger", children: error }), widgets("home:top"), making && (_jsx("section", { className: "sc-panel sc-panel-form", "aria-label": words.form.aria, children: _jsx(ProjectForm, { busy: busy, skillsSource: data.me?.workspace.skills.source, organizationAddress: data.me?.workspace.skills.address ?? null, onSubmit: (input, icon) => void act(async () => {
227
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sc-page-head", children: [_jsx("h1", { className: "sc-page-title", children: words.title }), !arranging && (_jsxs("div", { className: "sc-page-actions", children: [me !== undefined && me.person !== null && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setArranging(true), children: homeWords.edit })), administrator && !making && (_jsx(LinkButton, { href: PATHS.newProject, variant: "primary", children: words.newProject }))] }))] }), error !== undefined && _jsx(Callout, { tone: "danger", children: error }), making && (_jsx("section", { className: "sc-panel sc-panel-form", "aria-label": words.form.aria, children: _jsx(ProjectForm, { busy: busy, skillsSource: data.me?.workspace.skills.source, organizationAddress: data.me?.workspace.skills.address ?? null, onSubmit: (input, icon) => void act(async () => {
215
228
  const made = await data.actions.createProject(input, icon instanceof File ? uploadOf(icon) : undefined);
216
229
  navigation.replace(projectHref(made.key));
217
- }), onCancel: () => navigation.back(PATHS.projects) }) })), _jsx(components.ProjectList, { projects: data.projects, projectHref: href, onOpen: (project) => navigation.go(href(project)), empty: _jsx(EmptyState, { title: words.empty.title, body: administrator ? words.empty.body : words.empty.bodyMember, action: administrator ? (_jsx(LinkButton, { href: PATHS.newProject, children: words.newProject })) : undefined }) }), widgets("home:bottom")] }));
230
+ }), onCancel: () => navigation.back(PATHS.projects) }) })), _jsx(components.HomeGrid, { projects: data.projects, plugins: data.plugins, home: me?.home, editing: arranging, onSave: (home) => data.actions.updateMe({ home }), onDone: () => setArranging(false), projectHref: href, onOpen: (project) => navigation.go(href(project)), nameOf: (name) => pluginWordOf(name, language), renderWidget: (tile, size) => me === undefined ? null : (_jsx(PluginFrame, { plugin: tile.plugin, widget: tile.widget.key, size: size, me: me, language: language, client: client, navigation: navigation, title: pluginWordOf(tile.widget.name, language) })), empty: _jsx(EmptyState, { title: words.empty.title, body: administrator ? words.empty.body : words.empty.bodyMember, action: administrator ? (_jsx(LinkButton, { href: PATHS.newProject, children: words.newProject })) : undefined }) })] }));
218
231
  }
219
232
  /**
220
233
  * A plugin's page (ADR-0054): the sub-tabs of its pages at the level, where it has several, above
@@ -233,7 +246,7 @@ function PluginPage(props) {
233
246
  return _jsx(EmptyState, { title: words.plugins.noPage.title, body: words.plugins.noPage.body });
234
247
  }
235
248
  const projectKey = route.level === "project" ? route.project : undefined;
236
- return (_jsxs(_Fragment, { children: [pages.length > 1 && (_jsx("nav", { className: "sc-subtabs", "aria-label": pluginWordOf(plugin.name, language), children: pages.map((candidate) => (_jsx("a", { href: pluginHref(plugin.key, candidate.key, projectKey), className: candidate.key === page.key ? "sc-tab sc-tab-current" : "sc-tab", "aria-current": candidate.key === page.key ? "page" : undefined, children: pluginWordOf(candidate.name, language) }, candidate.key))) })), _jsx(PluginFrame, { plugin: plugin, page: page.key, project: project, path: route.rest, me: data.me, language: language, client: client, navigation: navigation, events: project === undefined ? undefined : data.events, title: words.plugins.frame(pluginWordOf(page.name, language)) }, `${plugin.key}:${page.key}:${projectKey ?? ""}`)] }));
249
+ return (_jsxs(_Fragment, { children: [pages.length > 1 && (_jsx("nav", { className: "sc-page-tabs", "aria-label": pluginWordOf(plugin.name, language), children: pages.map((candidate) => (_jsx("a", { href: pluginHref(plugin.key, candidate.key, projectKey), className: candidate.key === page.key ? "sc-tab sc-tab-current" : "sc-tab", "aria-current": candidate.key === page.key ? "page" : undefined, children: pluginWordOf(candidate.name, language) }, candidate.key))) })), _jsx(PluginFrame, { plugin: plugin, page: page.key, project: project, path: route.rest, me: data.me, language: language, client: client, navigation: navigation, events: project === undefined ? undefined : data.events, title: words.plugins.frame(pluginWordOf(page.name, language)) }, `${plugin.key}:${page.key}:${projectKey ?? ""}`)] }));
237
250
  }
238
251
  /**
239
252
  * The plugins' settings (docs/specs/plugins.md, "The settings pages"): the list, or one plugin
@@ -256,9 +269,9 @@ function PluginSettingsPage(props) {
256
269
  setFailure(undefined);
257
270
  }
258
271
  catch (error) {
259
- setFailure(error instanceof ApiError ? error.message : words.common.somethingWentWrong);
272
+ setFailure(errorWords(error, words.common.somethingWentWrong, words));
260
273
  }
261
- }, [client, key, words.common.somethingWentWrong]);
274
+ }, [client, key, words]);
262
275
  useEffect(() => {
263
276
  setDetail(undefined);
264
277
  void read();
@@ -275,7 +288,7 @@ function PluginSettingsPage(props) {
275
288
  if (detail === undefined) {
276
289
  return (_jsx("div", { className: "sc-loading", children: _jsx(Spinner, { label: words.common.loading }) }));
277
290
  }
278
- return (_jsx(PluginSettingsView, { detail: detail, projects: data.projects, people: data.people, language: language, client: client, onChanged: async () => {
291
+ return (_jsx(PluginSettingsView, { detail: detail, projects: data.projects, people: data.people, plugins: data.plugins, teams: data.teams, language: language, client: client, onChanged: async () => {
279
292
  await read();
280
293
  await data.actions.refreshPlugins();
281
294
  }, onPurged: () => {
@@ -831,6 +844,41 @@ function PlatePage(props) {
831
844
  (plate.closed?.length ?? 0) === 0 &&
832
845
  (plate.decisions?.length ?? 0) === 0 ? (_jsx(EmptyState, { title: props.empty.title, body: props.empty.body })) : (_jsx(components.WorkList, { work: plate, week: props.week, weekStart: props.weekStart, taskHref: (projectKey, task) => taskHref(projectKey, task.number), decisionHref: (projectKey, decisionId) => decisionHref(projectKey, decisionId), weekHref: props.weekHref }))] }));
833
846
  }
847
+ /**
848
+ * One decision wherever it is (ADR-0068): in a project the person may see, or one that reaches
849
+ * them from elsewhere, addressed to them, raised by them, or a plugin's addressed to someone
850
+ * they manage; answered here where they may. The address a plugin's page sends a person to,
851
+ * who may not be in the project the decision waits in.
852
+ */
853
+ function MyDecisionPage(props) {
854
+ const words = useWords();
855
+ const { id, data } = props;
856
+ const { reachDecision, answerReachedDecision } = data.actions;
857
+ const [reached, setReached] = useState(undefined);
858
+ const [problem, setProblem] = useState(undefined);
859
+ const { busy, error, act } = useAction();
860
+ const couldNotLoad = words.myDecisions.couldNotLoad;
861
+ useEffect(() => {
862
+ const controller = new AbortController();
863
+ setReached(undefined);
864
+ setProblem(undefined);
865
+ reachDecision(id, controller.signal).then((found) => {
866
+ if (!controller.signal.aborted) {
867
+ setReached(found);
868
+ }
869
+ }, (failure) => {
870
+ if (!controller.signal.aborted) {
871
+ setProblem(errorWords(failure, couldNotLoad, words));
872
+ }
873
+ });
874
+ return () => controller.abort();
875
+ }, [reachDecision, id, couldNotLoad, words]);
876
+ const shown = problem ?? error;
877
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sc-page-head", children: [_jsx("h1", { className: "sc-page-title", children: words.decisions.one }), _jsx(LinkButton, { href: PATHS.decisions, children: words.myDecisions.title })] }), shown !== undefined && _jsx(Callout, { tone: "danger", children: shown }), reached === undefined ? (shown === undefined && (_jsx("div", { className: "sc-loading", children: _jsx(Spinner, { label: words.myDecisions.loading }) }))) : (_jsx(ReachedDecision, { reached: reached, onAnswer: (_decision, input) => void act(async () => {
878
+ const answered = await answerReachedDecision(id, input);
879
+ setReached((current) => current === undefined ? current : { ...current, decision: answered });
880
+ }), busy: busy }))] }));
881
+ }
834
882
  /** What is on the person's own plate, across their projects. */
835
883
  /** How many of a person's decisions are read at a time. */
836
884
  const MY_DECISIONS_PAGE = 50;
@@ -878,17 +926,22 @@ function useMyDecisions(myDecisions, raised, couldNotLoad) {
878
926
  */
879
927
  function MyDecisionsPage(props) {
880
928
  const words = useWords();
881
- const { myDecisions, answerDecisionIn } = props.data.actions;
929
+ const { myDecisions, answerReachedDecision } = props.data.actions;
882
930
  const addressed = useMyDecisions(myDecisions, false, words.myDecisions.couldNotLoad);
883
931
  const raised = useMyDecisions(myDecisions, true, words.myDecisions.couldNotLoad);
884
932
  const { busy, error, act } = useAction();
885
933
  const problem = addressed.problem ?? raised.problem ?? error;
886
- const onAnswer = (projectKey, decision, input) => void act(async () => {
887
- await answerDecisionIn(projectKey, decision.id, input);
934
+ // Answered at the decision's own address (ADR-0068), so that one that reaches the person from a
935
+ // project they are not in, a plugin's up the reporting line or one they were named in before
936
+ // they left it, is answered here as it is read here.
937
+ const onAnswer = (_projectKey, decision, input) => void act(async () => {
938
+ await answerReachedDecision(decision.id, input);
888
939
  addressed.reload();
889
940
  raised.reload();
890
941
  });
891
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "sc-page-head", children: _jsx("h1", { className: "sc-page-title", children: words.myDecisions.title }) }), problem !== undefined && _jsx(Callout, { tone: "danger", children: problem }), addressed.items === undefined || raised.items === undefined ? (problem === undefined && (_jsx("div", { className: "sc-loading", children: _jsx(Spinner, { label: words.myDecisions.loading }) }))) : addressed.items.length === 0 && raised.items.length === 0 ? (_jsx(EmptyState, { title: words.myDecisions.empty.title, body: words.myDecisions.empty.body })) : (_jsx(MyDecisions, { addressed: { items: addressed.items, more: addressed.more, onMore: addressed.readMore }, raised: { items: raised.items, more: raised.more, onMore: raised.readMore }, decisionHref: decisionHref, fileHref: (projectKey, fileId) => projectPath(projectKey, "files", fileId), onAnswer: onAnswer, busy: busy || addressed.busy || raised.busy }))] }));
942
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "sc-page-head", children: _jsx("h1", { className: "sc-page-title", children: words.myDecisions.title }) }), problem !== undefined && _jsx(Callout, { tone: "danger", children: problem }), addressed.items === undefined || raised.items === undefined ? (problem === undefined && (_jsx("div", { className: "sc-loading", children: _jsx(Spinner, { label: words.myDecisions.loading }) }))) : addressed.items.length === 0 && raised.items.length === 0 ? (_jsx(EmptyState, { title: words.myDecisions.empty.title, body: words.myDecisions.empty.body })) : (_jsx(MyDecisions, { addressed: { items: addressed.items, more: addressed.more, onMore: addressed.readMore }, raised: { items: raised.items, more: raised.more, onMore: raised.readMore }, decisionHref: (projectKey, decisionId) => props.data.projects.some((project) => project.key === projectKey)
943
+ ? decisionHref(projectKey, decisionId)
944
+ : myDecisionHref(decisionId), onAnswer: onAnswer, busy: busy || addressed.busy || raised.busy }))] }));
892
945
  }
893
946
  function WorkPage(props) {
894
947
  const words = useWords();
@@ -922,6 +975,9 @@ function WorkspacePage(props) {
922
975
  if (route.name === "my-decisions") {
923
976
  return _jsx(MyDecisionsPage, { data: data });
924
977
  }
978
+ if (route.name === "my-decision") {
979
+ return _jsx(MyDecisionPage, { id: route.id, data: data });
980
+ }
925
981
  if (route.name === "person-work") {
926
982
  return _jsx(PersonWorkPage, { id: route.id, data: data, components: components, week: route.week });
927
983
  }
@@ -940,24 +996,56 @@ function WorkspacePage(props) {
940
996
  ? (category) => void act(() => data.actions.removeCategory(category.key))
941
997
  : undefined, empty: _jsx(EmptyState, { title: categories.empty.title, body: categories.empty.body }) })] }));
942
998
  }
943
- if (route.name === "people" || route.name === "new-listing") {
999
+ if (route.name === "people" ||
1000
+ route.name === "new-listing" ||
1001
+ route.name === "teams" ||
1002
+ route.name === "new-team") {
944
1003
  const me = data.me?.person ?? undefined;
945
1004
  const administrator = me?.role === "admin";
1005
+ // The people and the teams (ADR-0073) are two views of one page, each with its form.
1006
+ const onTeams = route.name === "teams" || route.name === "new-team";
946
1007
  const making = route.name === "new-listing" && administrator;
1008
+ const makingTeam = route.name === "new-team" && administrator;
947
1009
  // An owner sees the work of the people in their projects; the console refuses the rest.
948
1010
  const oversees = administrator || data.projects.some((project) => project.role === "owner");
949
1011
  // The list is what the policy shows (ADR-0035): everyone, or the people one shares a project with.
950
1012
  const narrowed = peopleVisibilityOf(data.me?.workspace) === "shared_projects" && !administrator;
951
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sc-page-head", children: [_jsx("h1", { className: "sc-page-title", children: words.people.title }), administrator && !making && (_jsx(LinkButton, { href: PATHS.newListing, variant: "primary", children: words.people.letIn }))] }), narrowed && _jsx("p", { className: "sc-lead", children: words.people.narrowed }), error !== undefined && _jsx(Callout, { tone: "danger", children: error }), making && (_jsx("section", { className: "sc-panel sc-panel-form", "aria-label": words.people.letInAria, children: _jsx(ListingForm, { providers: data.me?.signIn ?? [], busy: busy, onSubmit: (input) => void act(async () => {
1013
+ const views = words.people.views;
1014
+ const inCount = data.people.filter((person) => person.status === "active").length;
1015
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sc-page-head", children: [_jsx("h1", { className: "sc-page-title", children: words.people.title }), administrator && !onTeams && !making && (_jsx(LinkButton, { href: PATHS.newListing, variant: "primary", children: words.people.letIn })), administrator && onTeams && !makingTeam && (_jsx(LinkButton, { href: PATHS.newTeam, variant: "primary", children: words.teams.newTeam }))] }), _jsxs("nav", { className: "sc-subtabs sc-people-views", "aria-label": views.aria, children: [_jsxs("a", { className: cx("sc-tab", !onTeams && "sc-tab-current"), href: PATHS.people, "aria-current": onTeams ? undefined : "page", children: [views.people, _jsx("span", { className: "sc-tab-count", children: inCount })] }), _jsxs("a", { className: cx("sc-tab", onTeams && "sc-tab-current"), href: PATHS.teams, "aria-current": onTeams ? "page" : undefined, children: [views.teams, _jsx("span", { className: "sc-tab-count", children: data.teams.length })] })] }), narrowed && _jsx("p", { className: "sc-lead", children: words.people.narrowed }), error !== undefined && _jsx(Callout, { tone: "danger", children: error }), makingTeam && (_jsx("section", { className: "sc-panel sc-panel-form", "aria-label": words.teams.newAria, children: _jsx(TeamForm, { busy: busy, onSubmit: (input) => void act(async () => {
1016
+ await data.actions.createTeam(input);
1017
+ navigation.replace(PATHS.teams);
1018
+ }), onCancel: () => navigation.back(PATHS.teams) }) })), onTeams && (_jsx(components.TeamList, { teams: data.teams, people: data.people, busy: busy, onRename: administrator
1019
+ ? (team, name) => void act(() => data.actions.renameTeam(team.key, { name }))
1020
+ : undefined, onRemove: administrator
1021
+ ? (team) => {
1022
+ // The roles granted to the team go with it: asked first, as nothing brings them back.
1023
+ if (window.confirm(words.teams.removeAsk(team.name))) {
1024
+ void act(() => data.actions.removeTeam(team.key));
1025
+ }
1026
+ }
1027
+ : undefined, onAddMember: administrator
1028
+ ? (team, person) => void act(() => data.actions.addTeamMember(team.key, person.id))
1029
+ : undefined, onRemoveMember: administrator
1030
+ ? (team, person) => void act(() => data.actions.removeTeamMember(team.key, person.id))
1031
+ : undefined })), making && (_jsx("section", { className: "sc-panel sc-panel-form", "aria-label": words.people.letInAria, children: _jsx(ListingForm, { providers: data.me?.signIn ?? [], busy: busy, onSubmit: (input) => void act(async () => {
952
1032
  await data.actions.createListing(input);
953
1033
  navigation.replace(PATHS.people);
954
- }), onCancel: () => navigation.back(PATHS.people) }) })), _jsx(components.PeopleList, { people: data.people, listed: data.listed, organization: data.organization, me: me, busy: busy, onChangeRole: administrator
1034
+ }), onCancel: () => navigation.back(PATHS.people) }) })), !onTeams && (_jsx(components.PeopleList, { people: data.people, listed: data.listed, organization: data.organization, me: me, busy: busy, onChangeRole: administrator
955
1035
  ? (person, role) => void act(() => data.actions.updatePerson(person.id, { role }))
956
1036
  : undefined, onChangeStatus: administrator
957
1037
  ? (person, status) => void act(() => data.actions.updatePerson(person.id, { status }))
1038
+ : undefined, onChangeManager: administrator
1039
+ ? (person, managerId) => void act(() => data.actions.updatePerson(person.id, { managerId }))
1040
+ : undefined, onChangeEmail: administrator
1041
+ ? (person, email) => void act(() => data.actions.updatePerson(person.id, { email }))
958
1042
  : undefined, onUnlist: administrator
959
1043
  ? (listing) => void act(() => data.actions.removeListing(listing.id))
960
- : undefined, workHref: oversees ? (person) => personWorkHref(person.id) : undefined, agentsHref: administrator ? (person) => personAgentsHref(person.id) : undefined })] }));
1044
+ : undefined, teams: data.teams, onJoinTeam: administrator
1045
+ ? (person, team) => void act(() => data.actions.addTeamMember(team.key, person.id))
1046
+ : undefined, onLeaveTeam: administrator
1047
+ ? (person, team) => void act(() => data.actions.removeTeamMember(team.key, person.id))
1048
+ : undefined, workHref: oversees ? (person) => personWorkHref(person.id) : undefined, agentsHref: administrator ? (person) => personAgentsHref(person.id) : undefined }))] }));
961
1049
  }
962
1050
  if (route.name === "person-agents") {
963
1051
  return _jsx(PersonAgentsPage, { id: route.id, data: data, components: components });
@@ -994,10 +1082,16 @@ export function createConsole(config = {}) {
994
1082
  const failure = signInFailureOf(location.search);
995
1083
  const title = data.me?.workspace.name ?? fallbackTitle;
996
1084
  const viaToken = holdsToken(data);
997
- // The language: the person's choice, else the browser's, else the first the console speaks.
1085
+ // The language: the person's own, else the browser's, else the first the console speaks.
998
1086
  const chosen = data.me?.language;
999
1087
  const pack = useMemo(() => chooseLanguage(chosen, typeof navigator === "undefined" ? [] : navigator.languages, languages), [chosen]);
1000
1088
  const words = pack.messages;
1089
+ const toKeep = languageToKeep(data.me, typeof navigator === "undefined" ? [] : navigator.languages, languages);
1090
+ useEffect(() => {
1091
+ if (toKeep !== undefined) {
1092
+ void data.actions.updateMe({ language: toKeep }).catch(() => undefined);
1093
+ }
1094
+ }, [toKeep, data.actions]);
1001
1095
  // An address that moved is shown at its new one, in its place.
1002
1096
  useEffect(() => {
1003
1097
  if (route.name === "moved") {
@@ -1012,7 +1106,7 @@ export function createConsole(config = {}) {
1012
1106
  }, [title, data.project, pack.tag]);
1013
1107
  let content;
1014
1108
  if (data.me === undefined) {
1015
- content = (_jsx(Ground, { children: _jsx("div", { className: "sc-loading", children: data.error === undefined ? (_jsx(Spinner, { label: words.common.loading })) : (_jsx(Callout, { tone: "danger", title: words.common.couldNotReach, action: _jsx(Button, { onClick: data.reload, children: words.common.tryAgain }), children: data.error })) }) }));
1109
+ content = (_jsx(Ground, { children: _jsx("div", { className: "sc-loading", children: data.error === undefined ? (_jsx(Spinner, { label: words.common.loading })) : (_jsx(Callout, { tone: "danger", title: words.common.couldNotReach, action: _jsx(Button, { onClick: data.reload, children: words.common.tryAgain }), children: errorWords(data.failure, data.error, words) })) }) }));
1016
1110
  }
1017
1111
  else if (data.me.person === null) {
1018
1112
  content = (_jsx(Ground, { children: _jsx(components.SignIn, { title: title, brand: config.brand, providers: data.me.signIn, returnTo: withoutSignInParam(location.pathname, location.search), failure: failure }) }));
@@ -1030,7 +1124,7 @@ export function createConsole(config = {}) {
1030
1124
  {
1031
1125
  href: PATHS.decisions,
1032
1126
  label: words.nav.myDecisions,
1033
- current: route.name === "my-decisions",
1127
+ current: route.name === "my-decisions" || route.name === "my-decision",
1034
1128
  count: data.me?.decisions?.waiting,
1035
1129
  },
1036
1130
  {
@@ -1038,6 +1132,8 @@ export function createConsole(config = {}) {
1038
1132
  label: words.nav.people,
1039
1133
  current: route.name === "people" ||
1040
1134
  route.name === "new-listing" ||
1135
+ route.name === "teams" ||
1136
+ route.name === "new-team" ||
1041
1137
  route.name === "person-agents",
1042
1138
  },
1043
1139
  ...(viaToken