@skillcdn/console 0.4.4 → 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 (192) hide show
  1. package/README.md +3 -3
  2. package/dist/api.d.ts +3 -3
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +3 -3
  5. package/dist/api.js.map +1 -1
  6. package/dist/cli/cli.d.ts.map +1 -1
  7. package/dist/cli/cli.js +162 -15
  8. package/dist/cli/cli.js.map +1 -1
  9. package/dist/cli/format.d.ts +16 -3
  10. package/dist/cli/format.d.ts.map +1 -1
  11. package/dist/cli/format.js +68 -19
  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 +26 -5
  15. package/dist/cli/help.js.map +1 -1
  16. package/dist/client.d.ts +28 -1
  17. package/dist/client.d.ts.map +1 -1
  18. package/dist/client.js +12 -2
  19. package/dist/client.js.map +1 -1
  20. package/dist/compat.js +1 -1
  21. package/dist/components/board.d.ts.map +1 -1
  22. package/dist/components/board.js +46 -15
  23. package/dist/components/board.js.map +1 -1
  24. package/dist/components/decision-form.d.ts +0 -3
  25. package/dist/components/decision-form.d.ts.map +1 -1
  26. package/dist/components/decision-form.js +3 -15
  27. package/dist/components/decision-form.js.map +1 -1
  28. package/dist/components/event-feed.d.ts.map +1 -1
  29. package/dist/components/event-feed.js +30 -10
  30. package/dist/components/event-feed.js.map +1 -1
  31. package/dist/components/my-decisions.d.ts +20 -3
  32. package/dist/components/my-decisions.d.ts.map +1 -1
  33. package/dist/components/my-decisions.js +14 -1
  34. package/dist/components/my-decisions.js.map +1 -1
  35. package/dist/components/people.d.ts +43 -1
  36. package/dist/components/people.d.ts.map +1 -1
  37. package/dist/components/people.js +117 -9
  38. package/dist/components/people.js.map +1 -1
  39. package/dist/components/picker.d.ts +8 -0
  40. package/dist/components/picker.d.ts.map +1 -1
  41. package/dist/components/picker.js +2 -2
  42. package/dist/components/picker.js.map +1 -1
  43. package/dist/components/plugin-frame.d.ts +0 -1
  44. package/dist/components/plugin-frame.d.ts.map +1 -1
  45. package/dist/components/plugin-frame.js +22 -1
  46. package/dist/components/plugin-frame.js.map +1 -1
  47. package/dist/components/plugin-settings.d.ts +5 -1
  48. package/dist/components/plugin-settings.d.ts.map +1 -1
  49. package/dist/components/plugin-settings.js +143 -30
  50. package/dist/components/plugin-settings.js.map +1 -1
  51. package/dist/components/projects.d.ts.map +1 -1
  52. package/dist/components/projects.js +3 -1
  53. package/dist/components/projects.js.map +1 -1
  54. package/dist/components/searches.d.ts +11 -0
  55. package/dist/components/searches.d.ts.map +1 -0
  56. package/dist/components/searches.js +30 -0
  57. package/dist/components/searches.js.map +1 -0
  58. package/dist/components/task-form.d.ts.map +1 -1
  59. package/dist/components/task-form.js +6 -5
  60. package/dist/components/task-form.js.map +1 -1
  61. package/dist/components/ui.d.ts +16 -0
  62. package/dist/components/ui.d.ts.map +1 -1
  63. package/dist/components/ui.js +10 -0
  64. package/dist/components/ui.js.map +1 -1
  65. package/dist/console.css +362 -67
  66. package/dist/console.d.ts +2 -1
  67. package/dist/console.d.ts.map +1 -1
  68. package/dist/console.js +90 -14
  69. package/dist/console.js.map +1 -1
  70. package/dist/data.d.ts +14 -1
  71. package/dist/data.d.ts.map +1 -1
  72. package/dist/data.js +36 -1
  73. package/dist/data.js.map +1 -1
  74. package/dist/i18n/en.d.ts +89 -0
  75. package/dist/i18n/en.d.ts.map +1 -1
  76. package/dist/i18n/en.js +87 -2
  77. package/dist/i18n/en.js.map +1 -1
  78. package/dist/i18n/ko.d.ts.map +1 -1
  79. package/dist/i18n/ko.js +87 -3
  80. package/dist/i18n/ko.js.map +1 -1
  81. package/dist/index.d.ts +3 -2
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +3 -2
  84. package/dist/index.js.map +1 -1
  85. package/dist/limits.d.ts +6 -0
  86. package/dist/limits.d.ts.map +1 -1
  87. package/dist/limits.js +6 -0
  88. package/dist/limits.js.map +1 -1
  89. package/dist/plugin/args.d.ts +6 -1
  90. package/dist/plugin/args.d.ts.map +1 -1
  91. package/dist/plugin/args.js +16 -11
  92. package/dist/plugin/args.js.map +1 -1
  93. package/dist/plugin/bridge.d.ts +20 -1
  94. package/dist/plugin/bridge.d.ts.map +1 -1
  95. package/dist/plugin/bridge.js +28 -0
  96. package/dist/plugin/bridge.js.map +1 -1
  97. package/dist/plugin/capabilities.d.ts +1 -1
  98. package/dist/plugin/capabilities.d.ts.map +1 -1
  99. package/dist/plugin/capabilities.js +10 -0
  100. package/dist/plugin/capabilities.js.map +1 -1
  101. package/dist/plugin/context.d.ts +158 -13
  102. package/dist/plugin/context.d.ts.map +1 -1
  103. package/dist/plugin/define.d.ts +13 -1
  104. package/dist/plugin/define.d.ts.map +1 -1
  105. package/dist/plugin/define.js +17 -1
  106. package/dist/plugin/define.js.map +1 -1
  107. package/dist/plugin/limits.d.ts +4 -0
  108. package/dist/plugin/limits.d.ts.map +1 -1
  109. package/dist/plugin/limits.js +4 -0
  110. package/dist/plugin/limits.js.map +1 -1
  111. package/dist/plugin/manifest.d.ts +15 -0
  112. package/dist/plugin/manifest.d.ts.map +1 -1
  113. package/dist/plugin/manifest.js +61 -4
  114. package/dist/plugin/manifest.js.map +1 -1
  115. package/dist/plugin/rest-schemas.d.ts +44 -0
  116. package/dist/plugin/rest-schemas.d.ts.map +1 -1
  117. package/dist/plugin/rest-schemas.js +55 -3
  118. package/dist/plugin/rest-schemas.js.map +1 -1
  119. package/dist/plugin/testing.d.ts +15 -1
  120. package/dist/plugin/testing.d.ts.map +1 -1
  121. package/dist/plugin/testing.js +40 -4
  122. package/dist/plugin/testing.js.map +1 -1
  123. package/dist/plugin/web.d.ts +6 -0
  124. package/dist/plugin/web.d.ts.map +1 -1
  125. package/dist/plugin/web.js +10 -1
  126. package/dist/plugin/web.js.map +1 -1
  127. package/dist/plugin.d.ts +1 -1
  128. package/dist/plugin.d.ts.map +1 -1
  129. package/dist/plugin.js +1 -1
  130. package/dist/plugin.js.map +1 -1
  131. package/dist/router.d.ts +19 -0
  132. package/dist/router.d.ts.map +1 -1
  133. package/dist/router.js +18 -0
  134. package/dist/router.js.map +1 -1
  135. package/dist/routes.d.ts +11 -2
  136. package/dist/routes.d.ts.map +1 -1
  137. package/dist/routes.js +14 -1
  138. package/dist/routes.js.map +1 -1
  139. package/dist/schemas.d.ts +367 -0
  140. package/dist/schemas.d.ts.map +1 -1
  141. package/dist/schemas.js +62 -2
  142. package/dist/schemas.js.map +1 -1
  143. package/dist/vocabulary.d.ts +1 -1
  144. package/dist/vocabulary.d.ts.map +1 -1
  145. package/dist/vocabulary.js +6 -0
  146. package/dist/vocabulary.js.map +1 -1
  147. package/docs/specs/cli.md +7 -4
  148. package/docs/specs/plugins.md +47 -39
  149. package/docs/specs/rest.md +22 -5
  150. package/package.json +1 -1
  151. package/skills/building-a-plugin/SKILL.md +8 -6
  152. package/skills/working-the-board/SKILL.md +1 -1
  153. package/src/api.ts +17 -0
  154. package/src/cli/cli.ts +200 -12
  155. package/src/cli/format.ts +82 -19
  156. package/src/cli/help.ts +30 -5
  157. package/src/client.ts +70 -0
  158. package/src/compat.ts +1 -1
  159. package/src/components/board.tsx +83 -42
  160. package/src/components/decision-form.tsx +13 -32
  161. package/src/components/event-feed.tsx +36 -9
  162. package/src/components/my-decisions.tsx +50 -5
  163. package/src/components/people.tsx +394 -37
  164. package/src/components/picker.tsx +11 -2
  165. package/src/components/plugin-frame.tsx +23 -0
  166. package/src/components/plugin-settings.tsx +346 -137
  167. package/src/components/projects.tsx +14 -14
  168. package/src/components/searches.ts +47 -0
  169. package/src/components/task-form.tsx +23 -28
  170. package/src/components/ui.tsx +36 -0
  171. package/src/console.tsx +229 -40
  172. package/src/data.ts +53 -1
  173. package/src/i18n/en.ts +98 -2
  174. package/src/i18n/ko.ts +95 -3
  175. package/src/index.ts +11 -0
  176. package/src/limits.ts +6 -0
  177. package/src/plugin/args.ts +16 -11
  178. package/src/plugin/bridge.ts +45 -0
  179. package/src/plugin/capabilities.ts +11 -0
  180. package/src/plugin/context.ts +170 -10
  181. package/src/plugin/define.ts +34 -0
  182. package/src/plugin/limits.ts +4 -0
  183. package/src/plugin/manifest.ts +74 -3
  184. package/src/plugin/rest-schemas.ts +62 -5
  185. package/src/plugin/testing.ts +61 -5
  186. package/src/plugin/web.ts +17 -0
  187. package/src/plugin.ts +1 -0
  188. package/src/router.ts +25 -0
  189. package/src/routes.ts +17 -2
  190. package/src/schemas.ts +81 -2
  191. package/src/styles/console.css +362 -67
  192. package/src/vocabulary.ts +6 -0
package/dist/console.js CHANGED
@@ -11,10 +11,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
13
  import { HomeGrid } from "./components/home.js";
14
- import { MyDecisions } from "./components/my-decisions.js";
14
+ import { MyDecisions, ReachedDecision } from "./components/my-decisions.js";
15
15
  import { PackTokenWays } from "./components/pack-token.js";
16
16
  import { PAGE_SIZE } from "./components/paged.js";
17
- import { ListingForm, PeopleList } from "./components/people.js";
17
+ import { ListingForm, PeopleList, TeamForm, TeamList, } from "./components/people.js";
18
18
  import { PluginFrame } from "./components/plugin-frame.js";
19
19
  import { PluginSettingsList, PluginSettingsView } from "./components/plugin-settings.js";
20
20
  import { MemberList, ProjectForm, ProjectMark, } from "./components/projects.js";
@@ -28,7 +28,7 @@ import { TaskForm } from "./components/task-form.js";
28
28
  import { TaskList } from "./components/task-list.js";
29
29
  import { TaskView } from "./components/task-view.js";
30
30
  import { NewToken, TokenForm, TokenList } from "./components/tokens.js";
31
- 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";
32
32
  import { WorkList } from "./components/work.js";
33
33
  import { peopleVisibilityOf, WorkspaceSettingsForm, WorkspaceSettingsView, weekStartOf, } from "./components/workspace.js";
34
34
  import { ConnectPage } from "./connect-page.js";
@@ -37,7 +37,7 @@ import { monthOf, today } from "./dates.js";
37
37
  import { DocsPage } from "./docs-page.js";
38
38
  import { browserPack, chooseLanguage, DEFAULT_LANGUAGES, LanguageContext, useWords, } from "./i18n/index.js";
39
39
  import { pluginNavItems, pluginPagesAt, pluginWordOf } from "./plugin-nav.js";
40
- 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";
41
41
  import { projectPath } from "./routes.js";
42
42
  import { errorWords, useAction } from "./use-action.js";
43
43
  export const DEFAULT_COMPONENTS = {
@@ -49,6 +49,7 @@ export const DEFAULT_COMPONENTS = {
49
49
  SignIn,
50
50
  TokenList,
51
51
  PeopleList,
52
+ TeamList,
52
53
  SkillList,
53
54
  HomeGrid,
54
55
  MemberList,
@@ -287,7 +288,7 @@ function PluginSettingsPage(props) {
287
288
  if (detail === undefined) {
288
289
  return (_jsx("div", { className: "sc-loading", children: _jsx(Spinner, { label: words.common.loading }) }));
289
290
  }
290
- 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 () => {
291
292
  await read();
292
293
  await data.actions.refreshPlugins();
293
294
  }, onPurged: () => {
@@ -843,6 +844,41 @@ function PlatePage(props) {
843
844
  (plate.closed?.length ?? 0) === 0 &&
844
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 }))] }));
845
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
+ }
846
882
  /** What is on the person's own plate, across their projects. */
847
883
  /** How many of a person's decisions are read at a time. */
848
884
  const MY_DECISIONS_PAGE = 50;
@@ -890,17 +926,22 @@ function useMyDecisions(myDecisions, raised, couldNotLoad) {
890
926
  */
891
927
  function MyDecisionsPage(props) {
892
928
  const words = useWords();
893
- const { myDecisions, answerDecisionIn } = props.data.actions;
929
+ const { myDecisions, answerReachedDecision } = props.data.actions;
894
930
  const addressed = useMyDecisions(myDecisions, false, words.myDecisions.couldNotLoad);
895
931
  const raised = useMyDecisions(myDecisions, true, words.myDecisions.couldNotLoad);
896
932
  const { busy, error, act } = useAction();
897
933
  const problem = addressed.problem ?? raised.problem ?? error;
898
- const onAnswer = (projectKey, decision, input) => void act(async () => {
899
- 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);
900
939
  addressed.reload();
901
940
  raised.reload();
902
941
  });
903
- 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 }))] }));
904
945
  }
905
946
  function WorkPage(props) {
906
947
  const words = useWords();
@@ -934,6 +975,9 @@ function WorkspacePage(props) {
934
975
  if (route.name === "my-decisions") {
935
976
  return _jsx(MyDecisionsPage, { data: data });
936
977
  }
978
+ if (route.name === "my-decision") {
979
+ return _jsx(MyDecisionPage, { id: route.id, data: data });
980
+ }
937
981
  if (route.name === "person-work") {
938
982
  return _jsx(PersonWorkPage, { id: route.id, data: data, components: components, week: route.week });
939
983
  }
@@ -952,26 +996,56 @@ function WorkspacePage(props) {
952
996
  ? (category) => void act(() => data.actions.removeCategory(category.key))
953
997
  : undefined, empty: _jsx(EmptyState, { title: categories.empty.title, body: categories.empty.body }) })] }));
954
998
  }
955
- 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") {
956
1003
  const me = data.me?.person ?? undefined;
957
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";
958
1007
  const making = route.name === "new-listing" && administrator;
1008
+ const makingTeam = route.name === "new-team" && administrator;
959
1009
  // An owner sees the work of the people in their projects; the console refuses the rest.
960
1010
  const oversees = administrator || data.projects.some((project) => project.role === "owner");
961
1011
  // The list is what the policy shows (ADR-0035): everyone, or the people one shares a project with.
962
1012
  const narrowed = peopleVisibilityOf(data.me?.workspace) === "shared_projects" && !administrator;
963
- 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 () => {
964
1032
  await data.actions.createListing(input);
965
1033
  navigation.replace(PATHS.people);
966
- }), 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
967
1035
  ? (person, role) => void act(() => data.actions.updatePerson(person.id, { role }))
968
1036
  : undefined, onChangeStatus: administrator
969
1037
  ? (person, status) => void act(() => data.actions.updatePerson(person.id, { status }))
970
1038
  : undefined, onChangeManager: administrator
971
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 }))
972
1042
  : undefined, onUnlist: administrator
973
1043
  ? (listing) => void act(() => data.actions.removeListing(listing.id))
974
- : 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 }))] }));
975
1049
  }
976
1050
  if (route.name === "person-agents") {
977
1051
  return _jsx(PersonAgentsPage, { id: route.id, data: data, components: components });
@@ -1050,7 +1124,7 @@ export function createConsole(config = {}) {
1050
1124
  {
1051
1125
  href: PATHS.decisions,
1052
1126
  label: words.nav.myDecisions,
1053
- current: route.name === "my-decisions",
1127
+ current: route.name === "my-decisions" || route.name === "my-decision",
1054
1128
  count: data.me?.decisions?.waiting,
1055
1129
  },
1056
1130
  {
@@ -1058,6 +1132,8 @@ export function createConsole(config = {}) {
1058
1132
  label: words.nav.people,
1059
1133
  current: route.name === "people" ||
1060
1134
  route.name === "new-listing" ||
1135
+ route.name === "teams" ||
1136
+ route.name === "new-team" ||
1061
1137
  route.name === "person-agents",
1062
1138
  },
1063
1139
  ...(viaToken