@parallelworks/tandem-web 0.142.0 → 0.143.0

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 (53) hide show
  1. package/_virtual/_virtual_i18n/en/common.js +105 -6
  2. package/_virtual/_virtual_i18n/es/common.js +105 -6
  3. package/_virtual/_virtual_i18n/ja/common.js +105 -6
  4. package/_virtual/_virtual_i18n/ko/common.js +105 -6
  5. package/_virtual/_virtual_i18n/zh/common.js +105 -6
  6. package/components/AccessGrants.js +6 -6
  7. package/components/ActivityTimeline.js +2 -0
  8. package/components/CodeHostSettings.js +25 -25
  9. package/components/CustomFields.js +349 -0
  10. package/components/DocProperties.js +8 -2
  11. package/components/GitHub.js +22 -22
  12. package/components/IssueBoard.js +48 -43
  13. package/components/IssueEvent.js +2 -0
  14. package/components/IssueFilters.js +210 -122
  15. package/components/IssueImport.js +1 -1
  16. package/components/IssueRow.js +96 -79
  17. package/components/IssueTemplates.js +216 -156
  18. package/components/IssueTypes.js +318 -0
  19. package/components/NewIssueDialog.js +254 -216
  20. package/components/SettingsNav.js +40 -35
  21. package/components/TeamForm.js +49 -35
  22. package/components/TeamRepositories.js +51 -51
  23. package/components/TeamWorkflow.js +8 -8
  24. package/components/Updates.js +8 -8
  25. package/lib/boardMove.js +69 -47
  26. package/lib/customFields.js +113 -0
  27. package/lib/issueDisplay.js +27 -12
  28. package/lib/issueList.js +166 -62
  29. package/lib/issueTypes.js +50 -0
  30. package/lib/teamTree.js +24 -0
  31. package/lib/views.js +13 -11
  32. package/package.json +1 -1
  33. package/pages/issues.js +331 -291
  34. package/routeTree.gen.js +151 -145
  35. package/routes/_workspace/agents/index2.js +9 -9
  36. package/routes/_workspace/issues/_identifier2.js +433 -370
  37. package/routes/_workspace/projects/_projectId3.js +213 -201
  38. package/routes/_workspace/settings/audit-log3.js +2 -0
  39. package/routes/_workspace/settings/fields.js +6 -0
  40. package/routes/_workspace/settings/fields2.js +30 -0
  41. package/routes/_workspace/settings/fields3.js +5 -0
  42. package/routes/_workspace/settings/groups2.js +74 -74
  43. package/routes/_workspace/settings/labels2.js +33 -33
  44. package/routes/_workspace/settings/members2.js +35 -35
  45. package/routes/_workspace/settings/roles2.js +24 -24
  46. package/routes/_workspace/settings/runners2.js +18 -18
  47. package/routes/_workspace/settings/sessions2.js +9 -9
  48. package/routes/_workspace/settings/slack2.js +8 -8
  49. package/routes/_workspace/settings/sso2.js +50 -50
  50. package/routes/_workspace/settings/tokens2.js +15 -15
  51. package/routes/_workspace/settings/webhooks2.js +42 -42
  52. package/routes/_workspace/teams/_key/settings2.js +138 -76
  53. package/routes/_workspace2.js +320 -318
@@ -116,6 +116,12 @@ function O({ workspace: e, property: n, value: r, editable: i, onChange: a }) {
116
116
  },
117
117
  className: "text-primary hover:underline",
118
118
  children: t
119
+ }) : n.kind === "url" && t ? /* @__PURE__ */ S("a", {
120
+ href: t,
121
+ target: "_blank",
122
+ rel: "noopener noreferrer",
123
+ className: "truncate text-primary hover:underline",
124
+ children: t
119
125
  }) : /* @__PURE__ */ S("span", {
120
126
  className: t ? "" : "text-muted-foreground",
121
127
  children: t || u
@@ -189,12 +195,12 @@ function k({ property: e, value: t, onChange: n }) {
189
195
  a !== i && (a.trim() ? n(e.kind === "number" ? Number(a) : a) : n(null));
190
196
  };
191
197
  return /* @__PURE__ */ S(d, {
192
- type: e.kind === "number" ? "number" : "text",
198
+ type: e.kind === "number" ? "number" : e.kind === "url" ? "url" : "text",
193
199
  value: a,
194
200
  onChange: (e) => o(e.target.value),
195
201
  onBlur: s,
196
202
  onKeyDown: (e) => e.key === "Enter" && s(),
197
- placeholder: r(e.kind === "issue" ? "issueExample" : "empty"),
203
+ placeholder: r(e.kind === "issue" ? "issueExample" : e.kind === "url" ? "urlExample" : "empty"),
198
204
  "aria-label": e.name,
199
205
  className: "h-7 border-transparent px-1.5 shadow-none hover:border-border"
200
206
  });
@@ -1,9 +1,9 @@
1
1
  import { basePath as e } from "../lib/base.js";
2
2
  import { api as t, unwrap as n } from "../api/client.js";
3
3
  import { githubAccountQuery as r, githubSettingsQuery as i, teamsQuery as a } from "../api/queries.js";
4
- import { CheckIcon as o, CodeHostIcon as s } from "./icons.js";
5
- import { cardList as c, useFormError as l } from "./settings.js";
6
- import { useConfirm as u } from "../lib/confirm.js";
4
+ import { useConfirm as o } from "../lib/confirm.js";
5
+ import { CheckIcon as s, CodeHostIcon as c } from "./icons.js";
6
+ import { cardList as l, useFormError as u } from "./settings.js";
7
7
  import { ConnectionCard as d } from "./ConnectionCard.js";
8
8
  import { TeamName as f } from "./TeamName.js";
9
9
  import { Button as p, ErrorText as m, ProgressBar as h, Select as g, SettingsCard as _, StatusLabel as v, Tooltip as y, buttonClasses as b } from "@parallelworks/ui";
@@ -44,15 +44,15 @@ async function I(e) {
44
44
  }
45
45
  var L = () => window.location.pathname.slice(e.length) || "/";
46
46
  function R() {
47
- let { data: e } = M(r), i = T("github"), a = j(), o = l(), c = P(), f = k({
47
+ let { data: e } = M(r), i = T("github"), a = j(), s = u(), l = P(), f = k({
48
48
  mutationFn: () => I(async () => n(await t.POST("/api/v1/me/github", { body: { returnTo: L() } }))),
49
- onMutate: o.clear,
50
- onError: o.set
49
+ onMutate: s.clear,
50
+ onError: s.set
51
51
  }), m = k({
52
52
  mutationFn: async () => n(await t.DELETE("/api/v1/me/github")),
53
- onMutate: o.clear,
53
+ onMutate: s.clear,
54
54
  onSettled: () => a.invalidateQueries({ queryKey: r.queryKey })
55
- }), { confirm: h, dialog: g } = u();
55
+ }), { confirm: h, dialog: g } = o();
56
56
  if (!e.enabled) return null;
57
57
  let _ = e.keepsTokens ? i("accountDescriptionBothWays") : i("accountDescription"), y = e.login ? /* @__PURE__ */ D(z, {
58
58
  login: e.login,
@@ -62,7 +62,7 @@ function R() {
62
62
  children: i("notLinkedLabel")
63
63
  });
64
64
  if (e.linkedByHost) return /* @__PURE__ */ D(d, {
65
- icon: /* @__PURE__ */ D(s, { className: "size-7" }),
65
+ icon: /* @__PURE__ */ D(c, { className: "size-7" }),
66
66
  name: i("accountTitle"),
67
67
  status: /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D("span", {
68
68
  className: "block",
@@ -79,10 +79,10 @@ function R() {
79
79
  });
80
80
  let S = !!e.login && e.keepsTokens && !e.actsAsYou;
81
81
  return /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D(d, {
82
- icon: /* @__PURE__ */ D(s, { className: "size-7" }),
82
+ icon: /* @__PURE__ */ D(c, { className: "size-7" }),
83
83
  name: i("accountTitle"),
84
84
  status: y,
85
- error: o.error,
85
+ error: s.error,
86
86
  action: e.login ? /* @__PURE__ */ D(p, {
87
87
  variant: "outline",
88
88
  type: "button",
@@ -101,7 +101,7 @@ function R() {
101
101
  onClick: () => f.mutate(),
102
102
  children: i("link")
103
103
  })] }),
104
- children: (c || S) && /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D(F, { outcome: c }), S && /* @__PURE__ */ O("div", {
104
+ children: (l || S) && /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D(F, { outcome: l }), S && /* @__PURE__ */ O("div", {
105
105
  className: "flex items-center gap-3 rounded-lg border border-border px-4 py-3 text-sm",
106
106
  children: [/* @__PURE__ */ D("p", {
107
107
  className: "min-w-0 flex-1",
@@ -134,7 +134,7 @@ function B({ workspace: e }) {
134
134
  title: r("settingsTitle"),
135
135
  description: r("settingsDescription"),
136
136
  children: /* @__PURE__ */ O("ul", {
137
- className: c,
137
+ className: l,
138
138
  children: [t.repositories.map((t) => {
139
139
  let i = n.find((e) => e.key === t.team)?.name ?? t.team;
140
140
  return /* @__PURE__ */ D("li", {
@@ -167,8 +167,8 @@ function B({ workspace: e }) {
167
167
  }) : null;
168
168
  }
169
169
  var V = "", H = 2e3;
170
- function U({ workspace: e, repository: r, teamName: s, children: c }) {
171
- let d = T("github"), h = j(), _ = l(), { confirm: v, dialog: y } = u(), { data: b } = A(i(e)), { data: C = [] } = A(a(e)), w = (e) => C.find((t) => t.key === e) ?? {
170
+ function U({ workspace: e, repository: r, teamName: c, children: l }) {
171
+ let d = T("github"), h = j(), _ = u(), { confirm: v, dialog: y } = o(), { data: b } = A(i(e)), { data: C = [] } = A(a(e)), w = (e) => C.find((t) => t.key === e) ?? {
172
172
  icon: "",
173
173
  name: e
174
174
  }, M = (e) => w(e).name, N = (e) => () => /* @__PURE__ */ D(f, { team: w(e) }), P = (b?.repositories ?? []).filter((e) => e.id === r.id && e.team !== r.team), F = P.find((e) => e.syncMode), [I, L] = S(!1);
@@ -224,12 +224,12 @@ function U({ workspace: e, repository: r, teamName: s, children: c }) {
224
224
  repository: r.fullName
225
225
  }),
226
226
  description: /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D("p", { children: d.rich("takeOverIntro", {
227
- teamName: s,
227
+ teamName: c,
228
228
  team: N(r.team)
229
229
  }) }), /* @__PURE__ */ O("ul", {
230
230
  className: "mt-2 list-disc space-y-1 pl-5",
231
231
  children: [/* @__PURE__ */ D("li", { children: d.rich("takeOverNew", {
232
- teamName: s,
232
+ teamName: c,
233
233
  team: N(r.team)
234
234
  }) }), F.imported > 0 && /* @__PURE__ */ D("li", { children: d.rich("takeOverKeeps", {
235
235
  fromName: t,
@@ -237,7 +237,7 @@ function U({ workspace: e, repository: r, teamName: s, children: c }) {
237
237
  count: F.imported
238
238
  }) })]
239
239
  })] }),
240
- confirmLabel: d("takeOver", { team: s }),
240
+ confirmLabel: d("takeOver", { team: c }),
241
241
  destructive: !1,
242
242
  onConfirm: () => B.mutateAsync({
243
243
  syncMode: e,
@@ -255,12 +255,12 @@ function U({ workspace: e, repository: r, teamName: s, children: c }) {
255
255
  /* @__PURE__ */ O("div", {
256
256
  className: "flex flex-wrap items-center gap-2",
257
257
  children: [
258
- c,
258
+ l,
259
259
  I && /* @__PURE__ */ O("span", {
260
260
  role: "status",
261
261
  className: "flex items-center gap-1 text-xs text-muted-foreground",
262
262
  children: [
263
- /* @__PURE__ */ D(o, { className: "size-3.5" }),
263
+ /* @__PURE__ */ D(s, { className: "size-3.5" }),
264
264
  " ",
265
265
  d("saved")
266
266
  ]
@@ -268,7 +268,7 @@ function U({ workspace: e, repository: r, teamName: s, children: c }) {
268
268
  /* @__PURE__ */ D(g, {
269
269
  "aria-label": d("modeFor", {
270
270
  repository: r.fullName,
271
- team: s
271
+ team: c
272
272
  }),
273
273
  value: K,
274
274
  disabled: B.isPending,
@@ -411,7 +411,7 @@ function q({ links: e }) {
411
411
  target: "_blank",
412
412
  rel: "noreferrer",
413
413
  className: "inline-flex h-6 min-w-0 items-center gap-1.5 rounded-md border border-border px-1.5 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
414
- children: [/* @__PURE__ */ D(s, { className: "size-3.5" }), /* @__PURE__ */ D("span", {
414
+ children: [/* @__PURE__ */ D(c, { className: "size-3.5" }), /* @__PURE__ */ D("span", {
415
415
  className: "truncate",
416
416
  children: J(e.url)
417
417
  })]
@@ -1,53 +1,54 @@
1
1
  import { Link as e } from "../lib/router.js";
2
2
  import { PriorityIcon as t, ProjectIcon as n } from "./icons.js";
3
- import { usePeople as r } from "../lib/people.js";
4
- import { Avatar as i } from "@parallelworks/ui";
5
- import { useState as a } from "react";
6
- import { useTranslations as o } from "use-intl";
7
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
3
+ import { TypeIcon as r } from "./IssueTypes.js";
4
+ import { usePeople as i } from "../lib/people.js";
5
+ import { Avatar as a } from "@parallelworks/ui";
6
+ import { useState as o } from "react";
7
+ import { useTranslations as s } from "use-intl";
8
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
8
9
  // src/components/IssueBoard.tsx
9
- function l({ workspace: e, columns: t, onMove: n, onContextMenu: r }) {
10
- let i = o("issues"), [l, d] = a(null), [f, p] = a(null);
11
- return /* @__PURE__ */ s("ul", {
10
+ function u({ workspace: e, columns: t, onMove: n, onContextMenu: r }) {
11
+ let i = s("issues"), [a, u] = o(null), [f, p] = o(null);
12
+ return /* @__PURE__ */ c("ul", {
12
13
  className: "flex h-full min-h-0 snap-x gap-3 overflow-x-auto p-3 md:p-4",
13
14
  "aria-label": i("board"),
14
15
  children: t.map((t) => {
15
- let i = !!l && !!n && !t.issues.some((e) => e.id === l.issue.id);
16
- return /* @__PURE__ */ c("li", {
16
+ let i = !!a && !!n && !t.issues.some((e) => e.id === a.issue.id);
17
+ return /* @__PURE__ */ l("li", {
17
18
  "aria-label": t.label,
18
19
  onDragOver: (e) => {
19
20
  i && (e.preventDefault(), e.dataTransfer.dropEffect = "move", p(t.key));
20
21
  },
21
22
  onDragLeave: () => p((e) => e === t.key ? null : e),
22
23
  onDrop: (e) => {
23
- e.preventDefault(), i && l && n?.(l.issue, l.from, t.key), d(null), p(null);
24
+ e.preventDefault(), i && a && n?.(a.issue, a.from, t.key), u(null), p(null);
24
25
  },
25
26
  className: `flex max-h-full min-w-60 flex-1 basis-0 snap-start flex-col rounded-lg border bg-muted/30 ${f === t.key ? "border-primary" : "border-border"}`,
26
- children: [/* @__PURE__ */ c("h2", {
27
+ children: [/* @__PURE__ */ l("h2", {
27
28
  className: "flex h-9 shrink-0 items-center gap-2 px-3 text-xs font-medium",
28
29
  children: [
29
30
  t.icon,
30
- /* @__PURE__ */ s("span", {
31
+ /* @__PURE__ */ c("span", {
31
32
  className: "truncate",
32
33
  children: t.label
33
34
  }),
34
- /* @__PURE__ */ s("span", {
35
+ /* @__PURE__ */ c("span", {
35
36
  className: "text-muted-foreground tabular-nums",
36
37
  children: t.issues.length
37
38
  })
38
39
  ]
39
- }), /* @__PURE__ */ s("ul", {
40
+ }), /* @__PURE__ */ c("ul", {
40
41
  className: "min-h-12 flex-1 space-y-2 overflow-y-auto px-2 pb-2",
41
- children: t.issues.map((i) => /* @__PURE__ */ s(u, {
42
+ children: t.issues.map((i) => /* @__PURE__ */ c(d, {
42
43
  workspace: e,
43
44
  issue: i,
44
45
  draggable: !!n,
45
- onDragStart: () => d({
46
+ onDragStart: () => u({
46
47
  issue: i,
47
48
  from: t.key
48
49
  }),
49
50
  onDragEnd: () => {
50
- d(null), p(null);
51
+ u(null), p(null);
51
52
  },
52
53
  onContextMenu: (e) => r(e, i)
53
54
  }, i.id))
@@ -56,54 +57,58 @@ function l({ workspace: e, columns: t, onMove: n, onContextMenu: r }) {
56
57
  })
57
58
  });
58
59
  }
59
- function u({ workspace: a, issue: o, draggable: l, onDragStart: u, onDragEnd: d, onContextMenu: f }) {
60
- let { nameOf: p, avatarOf: m } = r(a), h = o.assignees[0];
61
- return /* @__PURE__ */ s("li", { children: /* @__PURE__ */ c(e, {
60
+ function d({ workspace: o, issue: s, draggable: u, onDragStart: d, onDragEnd: f, onContextMenu: p }) {
61
+ let { nameOf: m, avatarOf: h } = i(o), g = s.assignees[0];
62
+ return /* @__PURE__ */ c("li", { children: /* @__PURE__ */ l(e, {
62
63
  to: "/$workspace/issues/$identifier",
63
64
  params: {
64
- workspace: a,
65
- identifier: o.identifier
65
+ workspace: o,
66
+ identifier: s.identifier
66
67
  },
67
- draggable: l,
68
+ draggable: u,
68
69
  onDragStart: (e) => {
69
- e.dataTransfer.effectAllowed = "move", e.dataTransfer.setData("text/plain", o.identifier), u();
70
+ e.dataTransfer.effectAllowed = "move", e.dataTransfer.setData("text/plain", s.identifier), d();
70
71
  },
71
- onDragEnd: d,
72
- onContextMenu: f,
72
+ onDragEnd: f,
73
+ onContextMenu: p,
73
74
  className: "block space-y-2 rounded-md border border-border bg-background p-2.5 text-sm shadow-xs hover:border-foreground/30",
74
75
  children: [
75
- /* @__PURE__ */ c("span", {
76
+ /* @__PURE__ */ l("span", {
76
77
  className: "flex items-center gap-2 text-xs text-muted-foreground",
77
78
  children: [
78
- /* @__PURE__ */ s(t, { priority: o.priority }),
79
- /* @__PURE__ */ s("span", { children: o.identifier }),
80
- h && /* @__PURE__ */ s(i, {
81
- name: p(h.kind, h.handle),
82
- src: m(h.kind, h.handle),
79
+ /* @__PURE__ */ c(t, { priority: s.priority }),
80
+ s.type && /* @__PURE__ */ c(r, {
81
+ type: s.type,
82
+ className: "size-3.5"
83
+ }),
84
+ /* @__PURE__ */ c("span", { children: s.identifier }),
85
+ g && /* @__PURE__ */ c(a, {
86
+ name: m(g.kind, g.handle),
87
+ src: h(g.kind, g.handle),
83
88
  size: "xs",
84
89
  className: "ml-auto"
85
90
  })
86
91
  ]
87
92
  }),
88
- /* @__PURE__ */ s("span", {
93
+ /* @__PURE__ */ c("span", {
89
94
  className: "line-clamp-3 block",
90
- children: o.title
95
+ children: s.title
91
96
  }),
92
- (o.project || o.labels.length > 0) && /* @__PURE__ */ c("span", {
97
+ (s.project || s.labels.length > 0) && /* @__PURE__ */ l("span", {
93
98
  className: "flex flex-wrap items-center gap-1.5 text-[11px] text-muted-foreground",
94
- children: [o.project && /* @__PURE__ */ c("span", {
99
+ children: [s.project && /* @__PURE__ */ l("span", {
95
100
  className: "inline-flex max-w-full items-center gap-1 truncate",
96
- children: [/* @__PURE__ */ s(n, { className: "size-3 shrink-0" }), /* @__PURE__ */ s("span", {
101
+ children: [/* @__PURE__ */ c(n, { className: "size-3 shrink-0" }), /* @__PURE__ */ c("span", {
97
102
  className: "truncate",
98
- children: o.project.name
103
+ children: s.project.name
99
104
  })]
100
- }), o.labels.map((e) => /* @__PURE__ */ c("span", {
105
+ }), s.labels.map((e) => /* @__PURE__ */ l("span", {
101
106
  title: e.name,
102
107
  className: "inline-flex max-w-full min-w-0 items-center gap-1 rounded-full border border-border px-1.5",
103
- children: [/* @__PURE__ */ s("span", {
108
+ children: [/* @__PURE__ */ c("span", {
104
109
  className: "size-1.5 shrink-0 rounded-full",
105
110
  style: { background: e.color }
106
- }), /* @__PURE__ */ s("span", {
111
+ }), /* @__PURE__ */ c("span", {
107
112
  className: "truncate",
108
113
  children: e.name
109
114
  })]
@@ -112,4 +117,4 @@ function u({ workspace: a, issue: o, draggable: l, onDragStart: u, onDragEnd: d,
112
117
  ]
113
118
  }) });
114
119
  }
115
- export { l as IssueBoard };
120
+ export { u as IssueBoard };
@@ -97,6 +97,8 @@ function f({ workspace: f, event: p }) {
97
97
  case "issue.cycle_changed": return C.number ? T("cycleSet", { number: String(C.number) }) : T("cycleCleared");
98
98
  case "issue.estimate_changed": return Number(C.to) ? T("estimateSet", { points: _.number(Number(C.to)) }) : T("estimateCleared");
99
99
  case "issue.due_date_changed": return C.to ? T("dueDateSet", { date: b.day(String(C.to)) }) : T("dueDateCleared");
100
+ case "issue.type_changed": return C.to ? T("typeChanged", { type: String(C.to) }) : T("typeCleared");
101
+ case "issue.fields_changed": return T("fieldsChanged", { fields: _.list(C.fields ?? []) });
100
102
  case "issue.label_added": return T("labelAdded", { label: String(C.name) });
101
103
  case "issue.label_removed": return T("labelRemoved", { label: String(C.name) });
102
104
  case "run.created": return T("runCreated");