@parallelworks/tandem-web 0.143.0 → 0.144.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.
@@ -0,0 +1,330 @@
1
+ import { api as e, unwrap as t } from "../../../api/client.js";
2
+ import { linearImportQuery as n, teamsQuery as r } from "../../../api/queries.js";
3
+ import { useConfirm as i } from "../../../lib/confirm.js";
4
+ import { MigrationIcon as a } from "../../../components/icons.js";
5
+ import { Route as o } from "./import.js";
6
+ import { useCan as s } from "../../../lib/roles.js";
7
+ import { AdminOnly as c, SettingsPage as l, useFormError as u } from "../../../components/settings.js";
8
+ import { Button as d, Checkbox as f, Disclosure as p, ErrorText as m, Field as h, Input as g, ProgressBar as _, Select as v, SettingsCard as y, StatusLabel as b } from "@parallelworks/ui";
9
+ import { useState as x } from "react";
10
+ import { useFormatter as S, useTranslations as C } from "use-intl";
11
+ import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
12
+ import { useMutation as D, useQueryClient as O, useSuspenseQuery as k } from "@tanstack/react-query";
13
+ // src/routes/$workspace/settings/import.tsx?tsr-split=component
14
+ function A() {
15
+ let { workspace: e } = o.useParams(), { data: t } = k(n(e)), r = s(e, "manage_workspace"), i = C("settingsNav"), a = C("linearImport"), u = t.import?.status === "running";
16
+ return /* @__PURE__ */ E(l, {
17
+ title: i("import"),
18
+ description: a("pageDescription"),
19
+ children: [/* @__PURE__ */ T(c, { workspace: e }), t.enabled ? /* @__PURE__ */ E("div", {
20
+ className: "space-y-6",
21
+ children: [
22
+ t.import && /* @__PURE__ */ T(M, {
23
+ workspace: e,
24
+ im: t.import
25
+ }),
26
+ r && !u && /* @__PURE__ */ T(P, {
27
+ workspace: e,
28
+ again: !!t.import
29
+ }),
30
+ /* @__PURE__ */ E("section", {
31
+ className: "space-y-2 text-sm",
32
+ children: [/* @__PURE__ */ T("h2", {
33
+ className: "font-medium",
34
+ children: a("howTitle")
35
+ }), /* @__PURE__ */ E("ul", {
36
+ className: "list-disc space-y-1.5 pl-5 text-muted-foreground",
37
+ children: [
38
+ /* @__PURE__ */ T("li", { children: a("howIssues") }),
39
+ /* @__PURE__ */ T("li", { children: a("howWorkflow") }),
40
+ /* @__PURE__ */ T("li", { children: a("howProjects") }),
41
+ /* @__PURE__ */ T("li", { children: a("howPeople") })
42
+ ]
43
+ })]
44
+ })
45
+ ]
46
+ }) : /* @__PURE__ */ T(y, { children: /* @__PURE__ */ T("p", {
47
+ className: "px-3 py-2 text-sm text-muted-foreground",
48
+ children: a("notConfigured")
49
+ }) })]
50
+ });
51
+ }
52
+ var j = {
53
+ running: "info",
54
+ done: "success",
55
+ failed: "error",
56
+ canceled: "muted"
57
+ };
58
+ function M({ workspace: r, im: o }) {
59
+ let c = C("linearImport"), l = S(), f = s(r, "manage_workspace"), h = O(), g = u(), { confirm: v, dialog: x } = i(), w = D({
60
+ mutationFn: async () => t(await e.POST("/api/v1/workspaces/{workspace}/linear/import/cancel", { params: { path: { workspace: r } } })),
61
+ onMutate: g.clear,
62
+ onSuccess: () => h.invalidateQueries({ queryKey: n(r).queryKey }),
63
+ onError: g.set
64
+ }), k = o.status === "running";
65
+ return /* @__PURE__ */ E(y, { children: [x, /* @__PURE__ */ E("div", {
66
+ className: "space-y-3 px-3 py-3",
67
+ children: [
68
+ /* @__PURE__ */ E("div", {
69
+ className: "flex flex-wrap items-center gap-3",
70
+ children: [
71
+ /* @__PURE__ */ T(a, { className: "size-5 text-muted-foreground" }),
72
+ /* @__PURE__ */ E("div", {
73
+ className: "min-w-0 flex-1",
74
+ children: [/* @__PURE__ */ T("p", {
75
+ className: "text-sm font-medium",
76
+ children: c("lastImport", { organization: o.organization })
77
+ }), /* @__PURE__ */ T("p", {
78
+ className: "text-xs text-muted-foreground",
79
+ children: c("startedAt", { date: l.dateTime(new Date(o.startedAt), {
80
+ dateStyle: "medium",
81
+ timeStyle: "short"
82
+ }) })
83
+ })]
84
+ }),
85
+ /* @__PURE__ */ T(b, {
86
+ variant: j[o.status],
87
+ pulse: k,
88
+ children: c(`status.${o.status}`)
89
+ }),
90
+ f && k && /* @__PURE__ */ T(d, {
91
+ type: "button",
92
+ size: "sm",
93
+ variant: "outline",
94
+ loading: w.isPending,
95
+ onClick: () => v({
96
+ title: c("cancelTitle"),
97
+ description: c("cancelDescription"),
98
+ confirmLabel: c("cancel"),
99
+ onConfirm: () => w.mutateAsync()
100
+ }),
101
+ children: c("cancel")
102
+ })
103
+ ]
104
+ }),
105
+ /* @__PURE__ */ T(_, {
106
+ value: o.total > 0 ? Math.min(o.processed, o.total) : k ? null : 1,
107
+ max: o.total > 0 ? o.total : 1,
108
+ label: c("issuesLabel"),
109
+ valueText: () => c("progress", {
110
+ processed: o.processed,
111
+ total: o.total
112
+ })
113
+ }),
114
+ /* @__PURE__ */ T("ul", {
115
+ className: "space-y-1 text-sm",
116
+ children: o.teams.map((e) => /* @__PURE__ */ T("li", {
117
+ className: "text-muted-foreground",
118
+ children: c("teamRow", {
119
+ source: `${e.sourceName} (${e.sourceKey})`,
120
+ team: e.teamKey ?? ""
121
+ })
122
+ }, e.sourceKey))
123
+ }),
124
+ o.error && /* @__PURE__ */ T("p", {
125
+ className: `text-sm ${k ? "text-muted-foreground" : "text-danger"}`,
126
+ children: c(k ? "retrying" : "stopped", { error: o.error })
127
+ }),
128
+ o.skipped > 0 && /* @__PURE__ */ T(p, {
129
+ summary: c("skipped", { count: o.skipped }),
130
+ children: /* @__PURE__ */ T("ul", {
131
+ className: "space-y-1 pt-2 text-sm",
132
+ children: o.skips.map((e, t) => /* @__PURE__ */ E("li", { children: [
133
+ /* @__PURE__ */ T("span", {
134
+ className: "font-medium",
135
+ children: e.item
136
+ }),
137
+ " ",
138
+ /* @__PURE__ */ T("span", {
139
+ className: "text-muted-foreground",
140
+ children: c(`skipReason.${e.reason}`, { field: e.field ?? "" })
141
+ })
142
+ ] }, t))
143
+ })
144
+ }),
145
+ /* @__PURE__ */ T(m, {
146
+ size: "md",
147
+ children: g.error
148
+ })
149
+ ]
150
+ })] });
151
+ }
152
+ var N = "__new__";
153
+ function P({ workspace: i, again: a }) {
154
+ let o = C("linearImport"), s = O(), { data: c } = k(r(i)), [l, p] = x(""), [_, b] = x(null), [S, A] = x({}), j = u(), M = c.filter((e) => !e.archivedAt), P = D({
155
+ mutationFn: async () => t(await e.POST("/api/v1/workspaces/{workspace}/linear/teams", {
156
+ params: { path: { workspace: i } },
157
+ body: { apiKey: l }
158
+ })),
159
+ onMutate: j.clear,
160
+ onSuccess: (e) => {
161
+ b(e), A(Object.fromEntries(e.teams.map((e) => {
162
+ let t = M.find((t) => t.key === e.key);
163
+ return [e.id, {
164
+ on: !0,
165
+ target: t?.key ?? N,
166
+ newKey: e.key,
167
+ newName: e.name
168
+ }];
169
+ })));
170
+ },
171
+ onError: j.set
172
+ }), F = D({
173
+ mutationFn: async () => t(await e.POST("/api/v1/workspaces/{workspace}/linear/import", {
174
+ params: { path: { workspace: i } },
175
+ body: {
176
+ apiKey: l,
177
+ teams: Object.entries(S).filter(([, e]) => e.on).map(([e, t]) => t.target === N ? {
178
+ sourceId: e,
179
+ newKey: t.newKey,
180
+ newName: t.newName
181
+ } : {
182
+ sourceId: e,
183
+ teamKey: t.target
184
+ })
185
+ }
186
+ })),
187
+ onMutate: j.clear,
188
+ onSuccess: () => {
189
+ p(""), b(null), s.invalidateQueries({ queryKey: n(i).queryKey }), s.invalidateQueries({ queryKey: r(i).queryKey });
190
+ },
191
+ onError: j.set
192
+ }), I = Object.values(S).filter((e) => e.on).length, L = (e, t) => A((n) => ({
193
+ ...n,
194
+ [e]: {
195
+ ...n[e],
196
+ ...t
197
+ }
198
+ }));
199
+ return /* @__PURE__ */ T(y, { children: /* @__PURE__ */ E("form", {
200
+ className: "space-y-4 px-3 py-3",
201
+ onSubmit: (e) => {
202
+ e.preventDefault(), _ ? F.mutate() : P.mutate();
203
+ },
204
+ children: [
205
+ /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("h2", {
206
+ className: "text-sm font-medium",
207
+ children: o(a ? "again" : "startTitle")
208
+ }), a && /* @__PURE__ */ T("p", {
209
+ className: "text-xs text-muted-foreground",
210
+ children: o("againHelp")
211
+ })] }),
212
+ /* @__PURE__ */ T(h, {
213
+ label: o("keyLabel"),
214
+ description: o("keyHelp"),
215
+ children: /* @__PURE__ */ T(g, {
216
+ type: "password",
217
+ autoComplete: "off",
218
+ required: !0,
219
+ value: l,
220
+ onChange: (e) => {
221
+ p(e.target.value), b(null);
222
+ },
223
+ placeholder: "lin_api_…"
224
+ })
225
+ }),
226
+ _ && /* @__PURE__ */ E("fieldset", {
227
+ className: "space-y-2",
228
+ children: [
229
+ /* @__PURE__ */ T("legend", {
230
+ className: "text-sm font-medium",
231
+ children: o("teamsFrom", { organization: _.organization })
232
+ }),
233
+ _.teams.length === 0 && /* @__PURE__ */ T("p", {
234
+ className: "text-sm text-muted-foreground",
235
+ children: o("noTeams")
236
+ }),
237
+ /* @__PURE__ */ T("ul", {
238
+ className: "divide-y divide-border rounded-md border border-border",
239
+ children: _.teams.map((e) => {
240
+ let t = S[e.id];
241
+ return t ? /* @__PURE__ */ E("li", {
242
+ className: "flex flex-wrap items-center gap-x-3 gap-y-2 px-3 py-2",
243
+ children: [/* @__PURE__ */ E("label", {
244
+ className: "flex min-w-0 flex-1 items-center gap-2 text-sm",
245
+ children: [
246
+ /* @__PURE__ */ T(f, {
247
+ checked: t.on,
248
+ onCheckedChange: (t) => L(e.id, { on: t })
249
+ }),
250
+ /* @__PURE__ */ E("span", {
251
+ className: "truncate",
252
+ children: [
253
+ e.name,
254
+ " ",
255
+ /* @__PURE__ */ E("span", {
256
+ className: "text-muted-foreground",
257
+ children: [
258
+ "(",
259
+ e.key,
260
+ ")"
261
+ ]
262
+ })
263
+ ]
264
+ }),
265
+ /* @__PURE__ */ T("span", {
266
+ className: "text-xs text-muted-foreground",
267
+ children: o("issues", { count: e.issueCount })
268
+ })
269
+ ]
270
+ }), t.on && /* @__PURE__ */ E("div", {
271
+ className: "flex basis-full flex-wrap items-center gap-2 pl-6 sm:basis-auto sm:pl-0",
272
+ children: [/* @__PURE__ */ T(v, {
273
+ "aria-label": o("into", { team: e.name }),
274
+ value: t.target,
275
+ onValueChange: (t) => t && L(e.id, { target: t }),
276
+ items: [{
277
+ value: N,
278
+ label: o("newTeam")
279
+ }, ...M.map((e) => ({
280
+ value: e.key,
281
+ label: `${e.name} (${e.key})`
282
+ }))],
283
+ size: "sm",
284
+ width: "content"
285
+ }), t.target === N && /* @__PURE__ */ E(w, { children: [/* @__PURE__ */ T(g, {
286
+ "aria-label": o("newKey"),
287
+ value: t.newKey,
288
+ onChange: (t) => L(e.id, { newKey: t.target.value.toUpperCase() }),
289
+ className: "w-24",
290
+ maxLength: 10,
291
+ required: !0
292
+ }), /* @__PURE__ */ T(g, {
293
+ "aria-label": o("newName"),
294
+ value: t.newName,
295
+ onChange: (t) => L(e.id, { newName: t.target.value }),
296
+ className: "w-44",
297
+ required: !0
298
+ })] })]
299
+ })]
300
+ }, e.id) : null;
301
+ })
302
+ })
303
+ ]
304
+ }),
305
+ /* @__PURE__ */ E("div", {
306
+ className: "flex flex-wrap items-center gap-3",
307
+ children: [_ ? /* @__PURE__ */ T(d, {
308
+ type: "submit",
309
+ variant: "primary",
310
+ loading: F.isPending,
311
+ disabled: I === 0,
312
+ children: o("start", { count: I })
313
+ }) : /* @__PURE__ */ T(d, {
314
+ type: "submit",
315
+ variant: "primary",
316
+ loading: P.isPending,
317
+ children: o("findTeams")
318
+ }), _ && /* @__PURE__ */ T("span", {
319
+ className: "text-xs text-muted-foreground",
320
+ children: o("total", { count: _.teams.filter((e) => S[e.id]?.on).reduce((e, t) => e + t.issueCount, 0) })
321
+ })]
322
+ }),
323
+ /* @__PURE__ */ T(m, {
324
+ size: "md",
325
+ children: j.error
326
+ })
327
+ ]
328
+ }) });
329
+ }
330
+ export { A as component };
@@ -0,0 +1,4 @@
1
+ import { linearImportQuery as e, teamsQuery as t } from "../../../api/queries.js";
2
+ // src/routes/$workspace/settings/import.tsx?tsr-split=loader
3
+ var n = ({ context: { queryClient: n }, params: { workspace: r } }) => Promise.all([n.ensureQueryData(e(r)), n.ensureQueryData(t(r))]);
4
+ export { n as loader };