@rudderhq/server 0.1.0-canary.9 → 0.1.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.
- package/dist/bootstrap/register-api-routes.d.ts.map +1 -1
- package/dist/bootstrap/register-api-routes.js +2 -0
- package/dist/bootstrap/register-api-routes.js.map +1 -1
- package/dist/bundled-plugins/plugin-linear/README.md +22 -0
- package/dist/bundled-plugins/plugin-linear/dist/manifest.js +183 -0
- package/dist/bundled-plugins/plugin-linear/dist/manifest.js.map +7 -0
- package/dist/bundled-plugins/plugin-linear/dist/ui/index.js +1229 -0
- package/dist/bundled-plugins/plugin-linear/dist/ui/index.js.map +7 -0
- package/dist/bundled-plugins/plugin-linear/dist/worker.js +8251 -0
- package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +7 -0
- package/dist/bundled-plugins/plugin-linear/package.json +42 -0
- package/dist/dev-server-status.d.ts +1 -7
- package/dist/dev-server-status.d.ts.map +1 -1
- package/dist/dev-server-status.js +1 -4
- package/dist/dev-server-status.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +77 -1
- package/dist/index.js.map +1 -1
- package/dist/langfuse-transcript.d.ts +1 -0
- package/dist/langfuse-transcript.d.ts.map +1 -1
- package/dist/langfuse-transcript.js +24 -0
- package/dist/langfuse-transcript.js.map +1 -1
- package/dist/onboarding-assets/ceo/MEMORY.md +13 -0
- package/dist/onboarding-assets/ceo/SOUL.md +28 -0
- package/dist/onboarding-assets/ceo/TOOLS.md +1 -1
- package/dist/onboarding-assets/default/MEMORY.md +13 -0
- package/dist/onboarding-assets/default/SOUL.md +29 -0
- package/dist/onboarding-assets/default/TOOLS.md +1 -1
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +4 -3
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/calendar.d.ts +3 -0
- package/dist/routes/calendar.d.ts.map +1 -0
- package/dist/routes/calendar.js +265 -0
- package/dist/routes/calendar.js.map +1 -0
- package/dist/routes/chats.d.ts.map +1 -1
- package/dist/routes/chats.js +149 -21
- package/dist/routes/chats.js.map +1 -1
- package/dist/routes/dashboard.d.ts.map +1 -1
- package/dist/routes/dashboard.js +24 -0
- package/dist/routes/dashboard.js.map +1 -1
- package/dist/routes/goals.d.ts.map +1 -1
- package/dist/routes/goals.js +10 -0
- package/dist/routes/goals.js.map +1 -1
- package/dist/routes/health.d.ts.map +1 -1
- package/dist/routes/health.js +3 -12
- package/dist/routes/health.js.map +1 -1
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/instance-settings.d.ts.map +1 -1
- package/dist/routes/instance-settings.js +1 -26
- package/dist/routes/instance-settings.js.map +1 -1
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +74 -34
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/orgs.d.ts.map +1 -1
- package/dist/routes/orgs.js +161 -145
- package/dist/routes/orgs.js.map +1 -1
- package/dist/routes/plugins.d.ts.map +1 -1
- package/dist/routes/plugins.js +30 -7
- package/dist/routes/plugins.js.map +1 -1
- package/dist/services/agent-instructions.d.ts.map +1 -1
- package/dist/services/agent-instructions.js +23 -7
- package/dist/services/agent-instructions.js.map +1 -1
- package/dist/services/agent-run-context.d.ts +1 -1
- package/dist/services/agent-run-context.js +1 -1
- package/dist/services/agent-run-context.js.map +1 -1
- package/dist/services/agents.d.ts +13 -13
- package/dist/services/assets.d.ts +2 -2
- package/dist/services/calendar.d.ts +137 -0
- package/dist/services/calendar.d.ts.map +1 -0
- package/dist/services/calendar.js +1279 -0
- package/dist/services/calendar.js.map +1 -0
- package/dist/services/chat-assistant.d.ts.map +1 -1
- package/dist/services/chat-assistant.js +75 -15
- package/dist/services/chat-assistant.js.map +1 -1
- package/dist/services/chat-generation-locks.d.ts +2 -1
- package/dist/services/chat-generation-locks.d.ts.map +1 -1
- package/dist/services/chat-generation-locks.js +12 -3
- package/dist/services/chat-generation-locks.js.map +1 -1
- package/dist/services/chats.d.ts +4 -2
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.js +2 -15
- package/dist/services/chats.js.map +1 -1
- package/dist/services/costs.d.ts +2 -2
- package/dist/services/default-agent-instructions.d.ts +2 -2
- package/dist/services/default-agent-instructions.js +2 -2
- package/dist/services/documents.d.ts +23 -0
- package/dist/services/documents.d.ts.map +1 -1
- package/dist/services/documents.js +17 -1
- package/dist/services/documents.js.map +1 -1
- package/dist/services/export-jobs.d.ts +16 -0
- package/dist/services/export-jobs.d.ts.map +1 -0
- package/dist/services/export-jobs.js +147 -0
- package/dist/services/export-jobs.js.map +1 -0
- package/dist/services/finance.d.ts +6 -6
- package/dist/services/goals.d.ts +16 -10
- package/dist/services/goals.d.ts.map +1 -1
- package/dist/services/goals.js +201 -18
- package/dist/services/goals.js.map +1 -1
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +3 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/instance-settings.d.ts +1 -3
- package/dist/services/instance-settings.d.ts.map +1 -1
- package/dist/services/instance-settings.js +1 -38
- package/dist/services/instance-settings.js.map +1 -1
- package/dist/services/issue-approvals.d.ts +1 -1
- package/dist/services/issues.d.ts +12 -0
- package/dist/services/issues.d.ts.map +1 -1
- package/dist/services/issues.js +107 -1
- package/dist/services/issues.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.d.ts +12 -2
- package/dist/services/knowledge-portability/organization-portability.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.js +77 -4
- package/dist/services/knowledge-portability/organization-portability.js.map +1 -1
- package/dist/services/messenger.d.ts +2 -2
- package/dist/services/messenger.d.ts.map +1 -1
- package/dist/services/messenger.js +67 -27
- package/dist/services/messenger.js.map +1 -1
- package/dist/services/organization-workspace-browser.d.ts.map +1 -1
- package/dist/services/organization-workspace-browser.js +3 -0
- package/dist/services/organization-workspace-browser.js.map +1 -1
- package/dist/services/plugin-registry.d.ts +8 -8
- package/dist/services/runtime-kernel/heartbeat.d.ts +6 -0
- package/dist/services/runtime-kernel/heartbeat.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.js +236 -99
- package/dist/services/runtime-kernel/heartbeat.js.map +1 -1
- package/dist/services/runtime-kernel/model-fallback.d.ts +10 -0
- package/dist/services/runtime-kernel/model-fallback.d.ts.map +1 -0
- package/dist/services/runtime-kernel/model-fallback.js +147 -0
- package/dist/services/runtime-kernel/model-fallback.js.map +1 -0
- package/dist/services/secrets.d.ts +1 -3
- package/dist/services/secrets.d.ts.map +1 -1
- package/dist/services/secrets.js +55 -30
- package/dist/services/secrets.js.map +1 -1
- package/dist/services/workspace-backups.d.ts +34 -0
- package/dist/services/workspace-backups.d.ts.map +1 -0
- package/dist/services/workspace-backups.js +519 -0
- package/dist/services/workspace-backups.js.map +1 -0
- package/dist/services/workspace-runtime.d.ts +2 -2
- package/package.json +14 -14
- package/resources/bundled-skills/para-memory-files/SKILL.md +3 -1
- package/resources/bundled-skills/rudder-create-agent/SKILL.md +21 -4
- package/resources/bundled-skills/rudder-create-agent/references/api-reference.md +8 -3
- package/resources/bundled-skills/rudder-create-agent/references/cli-reference.md +8 -2
- package/skills/para-memory-files/SKILL.md +3 -1
- package/skills/rudder-create-agent/SKILL.md +21 -4
- package/skills/rudder-create-agent/references/api-reference.md +8 -3
- package/skills/rudder-create-agent/references/cli-reference.md +8 -2
- package/ui-dist/assets/{_basePickBy-C5FevVGb.js → _basePickBy-9EA6dBFj.js} +1 -1
- package/ui-dist/assets/{_baseUniq-Bp5Cq-Lt.js → _baseUniq-puJRDjRm.js} +1 -1
- package/ui-dist/assets/{arc-DxCinQZQ.js → arc-BuvB_2Wz.js} +1 -1
- package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-Bt4OB6rg.js → architectureDiagram-2XIMDMQ5-DNH3NcPr.js} +1 -1
- package/ui-dist/assets/{blockDiagram-WCTKOSBZ-AfUyCHdW.js → blockDiagram-WCTKOSBZ-CCjA-egI.js} +1 -1
- package/ui-dist/assets/{c4Diagram-IC4MRINW-ZQmapm_f.js → c4Diagram-IC4MRINW-DaAxG30_.js} +1 -1
- package/ui-dist/assets/channel-BHmUwLHY.js +1 -0
- package/ui-dist/assets/{chunk-4BX2VUAB-b-nhg8XG.js → chunk-4BX2VUAB-CuuLnPLx.js} +1 -1
- package/ui-dist/assets/{chunk-55IACEB6-D_mWeaWL.js → chunk-55IACEB6-7KqKHU50.js} +1 -1
- package/ui-dist/assets/{chunk-FMBD7UC4-CvCBPkxY.js → chunk-FMBD7UC4-CquRnk_C.js} +1 -1
- package/ui-dist/assets/{chunk-JSJVCQXG-CyIzde6d.js → chunk-JSJVCQXG-Cub6UI-9.js} +1 -1
- package/ui-dist/assets/{chunk-KX2RTZJC-664uOAt1.js → chunk-KX2RTZJC-D-R4Pk61.js} +1 -1
- package/ui-dist/assets/{chunk-NQ4KR5QH-zC9eKlQL.js → chunk-NQ4KR5QH-YQLRgLCT.js} +1 -1
- package/ui-dist/assets/{chunk-QZHKN3VN-Bso6mrAm.js → chunk-QZHKN3VN-BgxQG6QM.js} +1 -1
- package/ui-dist/assets/{chunk-WL4C6EOR-CGgjDf4Q.js → chunk-WL4C6EOR-CVJNOFb-.js} +1 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-BykYYXhO.js +1 -0
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-BykYYXhO.js +1 -0
- package/ui-dist/assets/clone-BjbqkGJk.js +1 -0
- package/ui-dist/assets/{cose-bilkent-S5V4N54A-ChfhiHs0.js → cose-bilkent-S5V4N54A-BGYYdPRC.js} +1 -1
- package/ui-dist/assets/{dagre-KLK3FWXG-BtdGql15.js → dagre-KLK3FWXG-CDgRaJNK.js} +1 -1
- package/ui-dist/assets/{diagram-E7M64L7V-CcQq6lyW.js → diagram-E7M64L7V-CQEBiicN.js} +1 -1
- package/ui-dist/assets/{diagram-IFDJBPK2-C8MRQ8-O.js → diagram-IFDJBPK2-cGKTVrZq.js} +1 -1
- package/ui-dist/assets/{diagram-P4PSJMXO-wDtyafSS.js → diagram-P4PSJMXO-fGAfKBU_.js} +1 -1
- package/ui-dist/assets/{erDiagram-INFDFZHY-DSPOGKs9.js → erDiagram-INFDFZHY-DW5vJI98.js} +1 -1
- package/ui-dist/assets/{flowDiagram-PKNHOUZH-CMRO_o51.js → flowDiagram-PKNHOUZH-CikVuzCR.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-A5KZAMGK-ByVpG5X7.js → ganttDiagram-A5KZAMGK-Ca4perbO.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-C0hZhA2f.js → gitGraphDiagram-K3NZZRJ6-hkDkX0wB.js} +1 -1
- package/ui-dist/assets/{graph-8ZSpiLvu.js → graph-CKVwuNpm.js} +1 -1
- package/ui-dist/assets/{index-Jl3ZTphD.js → index-B24_1Y25.js} +1 -1
- package/ui-dist/assets/{index-Bnqrds93.js → index-BCSq0Y_A.js} +1 -1
- package/ui-dist/assets/{index-LxYtcd2q.js → index-BbX5RwLL.js} +1 -1
- package/ui-dist/assets/{index-BuxAGDe1.js → index-Bj5f8srw.js} +1 -1
- package/ui-dist/assets/{index-CrjKYwlq.js → index-Bm5RRuGQ.js} +1 -1
- package/ui-dist/assets/{index-Byt3a14a.js → index-BzHEDVXA.js} +1 -1
- package/ui-dist/assets/{index-C96r3ncF.js → index-C5IbLmrM.js} +1 -1
- package/ui-dist/assets/{index-DSa_Y_jA.js → index-CBuiHrHJ.js} +1 -1
- package/ui-dist/assets/{index-CsgWTWOx.js → index-CGtsmbZm.js} +1 -1
- package/ui-dist/assets/{index-Dolr9Kee.js → index-CIlRDiw5.js} +1 -1
- package/ui-dist/assets/{index-tGztn4Is.js → index-CT8eqX9W.js} +1 -1
- package/ui-dist/assets/{index-CeJdOYIF.js → index-CjD2xZdW.js} +1 -1
- package/ui-dist/assets/{index-C7DEZ3Ju.js → index-DFeHRm34.js} +1 -1
- package/ui-dist/assets/{index-ChJl_hqp.js → index-DI-FLO2Z.js} +1 -1
- package/ui-dist/assets/{index-D083o6by.js → index-DJ84yjUf.js} +1 -1
- package/ui-dist/assets/index-DTw34fFZ.js +1398 -0
- package/ui-dist/assets/{index--8IW0gQi.js → index-DZ6kUIBM.js} +1 -1
- package/ui-dist/assets/{index-D5fB3OrO.js → index-DdFp0EEO.js} +1 -1
- package/ui-dist/assets/{index-BYlbpnGO.js → index-Dm4kNTCW.js} +1 -1
- package/ui-dist/assets/{index-DIlroFT7.js → index-aK5eezHP.js} +1 -1
- package/ui-dist/assets/{index-DoCNo7J9.js → index-dd4k0fyq.js} +1 -1
- package/ui-dist/assets/index-jnv9Ql_2.css +1 -0
- package/ui-dist/assets/{index-Do2QEU2O.js → index-kGMjx6qb.js} +1 -1
- package/ui-dist/assets/{index-DGliz_Zl.js → index-qEEWalog.js} +1 -1
- package/ui-dist/assets/{infoDiagram-LFFYTUFH-CRObxa1Q.js → infoDiagram-LFFYTUFH-aDNdkSKW.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-ksXzVP6h.js → ishikawaDiagram-PHBUUO56-CmclzHhC.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-4ABVD52K-DhLhkeS3.js → journeyDiagram-4ABVD52K-BFnBxKuG.js} +1 -1
- package/ui-dist/assets/{kanban-definition-K7BYSVSG-CJPHwSur.js → kanban-definition-K7BYSVSG-eJfOZg7R.js} +1 -1
- package/ui-dist/assets/{layout-CbB6lAw2.js → layout-CLRiNHgA.js} +1 -1
- package/ui-dist/assets/{linear-HPte01nq.js → linear-B-J9sUer.js} +1 -1
- package/ui-dist/assets/{mermaid.core-CaHTquLw.js → mermaid.core-C1MjBOIN.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-YRQLILUH-CeZ9z-BE.js → mindmap-definition-YRQLILUH-BdvCmP6e.js} +1 -1
- package/ui-dist/assets/{pieDiagram-SKSYHLDU-YB621clF.js → pieDiagram-SKSYHLDU-BAITPD_t.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-337W2JSQ-KPDGBXfE.js → quadrantDiagram-337W2JSQ-BFnjyhzq.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-CnMP-_Rj.js → requirementDiagram-Z7DCOOCP-Bxg6tlLh.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK-rWDbj38-.js → sankeyDiagram-WA2Y5GQK-LPpklLQK.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-D5IlEfYm.js → sequenceDiagram-2WXFIKYE-D-W6lss0.js} +1 -1
- package/ui-dist/assets/{stateDiagram-RAJIS63D-CI6m7yMI.js → stateDiagram-RAJIS63D-Bzo5M8P7.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-DJ1MxF2S.js +1 -0
- package/ui-dist/assets/{timeline-definition-YZTLITO2-Bl1-YzON.js → timeline-definition-YZTLITO2-luuVqyTW.js} +1 -1
- package/ui-dist/assets/{treemap-KZPCXAKY-CcFSGzuM.js → treemap-KZPCXAKY-ChGqzx5u.js} +1 -1
- package/ui-dist/assets/{vennDiagram-LZ73GAT5-DpgfFxeZ.js → vennDiagram-LZ73GAT5-BCEjZinK.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-JWTSCODW-Bas4tWGP.js → xychartDiagram-JWTSCODW-mAsE6hMg.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/dist/onboarding-assets/ceo/AGENTS.md +0 -33
- package/dist/onboarding-assets/default/AGENTS.md +0 -9
- package/ui-dist/assets/channel-B-3UKZ6E.js +0 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-DJbF61vn.js +0 -1
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-DJbF61vn.js +0 -1
- package/ui-dist/assets/clone-B7Z_Fd8l.js +0 -1
- package/ui-dist/assets/index-B4jXCLTd.js +0 -1358
- package/ui-dist/assets/index-C187WwUh.css +0 -1
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-DMNsLapT.js +0 -1
|
@@ -0,0 +1,1229 @@
|
|
|
1
|
+
// src/ui/index.tsx
|
|
2
|
+
import {
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useState
|
|
6
|
+
} from "react";
|
|
7
|
+
import {
|
|
8
|
+
usePluginAction,
|
|
9
|
+
usePluginData,
|
|
10
|
+
usePluginToast
|
|
11
|
+
} from "@rudderhq/plugin-sdk/ui";
|
|
12
|
+
|
|
13
|
+
// src/constants.ts
|
|
14
|
+
var LINEAR_TOKEN_SETTINGS_URL = "https://linear.app/settings/account/security";
|
|
15
|
+
var LINEAR_PAGE_SIZE = 25;
|
|
16
|
+
var LINEAR_IMPORT_ALL_LIMIT = 100;
|
|
17
|
+
var DATA_KEYS = {
|
|
18
|
+
settingsBootstrap: "settings-bootstrap",
|
|
19
|
+
pageBootstrap: "page-bootstrap",
|
|
20
|
+
settingsCatalog: "settings-catalog",
|
|
21
|
+
catalog: "linear-catalog",
|
|
22
|
+
issues: "linear-issues",
|
|
23
|
+
issueLink: "issue-link"
|
|
24
|
+
};
|
|
25
|
+
var ACTION_KEYS = {
|
|
26
|
+
importIssues: "import-linear-issues"
|
|
27
|
+
};
|
|
28
|
+
var RUDDER_STATUS_OPTIONS = [
|
|
29
|
+
"backlog",
|
|
30
|
+
"todo",
|
|
31
|
+
"in_progress",
|
|
32
|
+
"in_review",
|
|
33
|
+
"done",
|
|
34
|
+
"blocked",
|
|
35
|
+
"cancelled"
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// src/ui/index.tsx
|
|
39
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
40
|
+
var layoutStyles = {
|
|
41
|
+
shell: {
|
|
42
|
+
display: "grid",
|
|
43
|
+
gap: 16,
|
|
44
|
+
color: "var(--foreground, #111827)",
|
|
45
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif"
|
|
46
|
+
},
|
|
47
|
+
card: {
|
|
48
|
+
border: "1px solid var(--border, rgba(15, 23, 42, 0.14))",
|
|
49
|
+
borderRadius: 8,
|
|
50
|
+
padding: 16,
|
|
51
|
+
background: "var(--card, var(--background, #fff))"
|
|
52
|
+
},
|
|
53
|
+
title: {
|
|
54
|
+
fontSize: 22,
|
|
55
|
+
fontWeight: 700,
|
|
56
|
+
margin: 0
|
|
57
|
+
},
|
|
58
|
+
subtitle: {
|
|
59
|
+
margin: "6px 0 0",
|
|
60
|
+
color: "var(--muted-foreground, rgba(15, 23, 42, 0.68))",
|
|
61
|
+
fontSize: 14,
|
|
62
|
+
lineHeight: 1.5
|
|
63
|
+
},
|
|
64
|
+
row: {
|
|
65
|
+
display: "flex",
|
|
66
|
+
gap: 12,
|
|
67
|
+
flexWrap: "wrap",
|
|
68
|
+
alignItems: "center"
|
|
69
|
+
},
|
|
70
|
+
connectionGrid: {
|
|
71
|
+
display: "grid",
|
|
72
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
|
|
73
|
+
gap: 12,
|
|
74
|
+
alignItems: "end"
|
|
75
|
+
},
|
|
76
|
+
field: {
|
|
77
|
+
display: "grid",
|
|
78
|
+
gap: 6,
|
|
79
|
+
minWidth: 180,
|
|
80
|
+
flex: "1 1 180px"
|
|
81
|
+
},
|
|
82
|
+
label: {
|
|
83
|
+
fontSize: 12,
|
|
84
|
+
fontWeight: 700,
|
|
85
|
+
color: "var(--muted-foreground, rgba(15, 23, 42, 0.66))",
|
|
86
|
+
letterSpacing: 0
|
|
87
|
+
},
|
|
88
|
+
input: {
|
|
89
|
+
width: "100%",
|
|
90
|
+
border: "1px solid var(--border, rgba(15, 23, 42, 0.18))",
|
|
91
|
+
borderRadius: 6,
|
|
92
|
+
padding: "10px 12px",
|
|
93
|
+
fontSize: 14,
|
|
94
|
+
background: "var(--background, #fff)",
|
|
95
|
+
color: "var(--foreground, #111827)",
|
|
96
|
+
boxSizing: "border-box"
|
|
97
|
+
},
|
|
98
|
+
select: {
|
|
99
|
+
width: "100%",
|
|
100
|
+
border: "1px solid var(--border, rgba(15, 23, 42, 0.18))",
|
|
101
|
+
borderRadius: 6,
|
|
102
|
+
padding: "10px 12px",
|
|
103
|
+
fontSize: 14,
|
|
104
|
+
background: "var(--background, #fff)",
|
|
105
|
+
color: "var(--foreground, #111827)",
|
|
106
|
+
boxSizing: "border-box"
|
|
107
|
+
},
|
|
108
|
+
button: {
|
|
109
|
+
border: "1px solid var(--border, rgba(15, 23, 42, 0.18))",
|
|
110
|
+
borderRadius: 6,
|
|
111
|
+
padding: "10px 14px",
|
|
112
|
+
fontSize: 14,
|
|
113
|
+
fontWeight: 600,
|
|
114
|
+
background: "var(--background, #fff)",
|
|
115
|
+
color: "var(--foreground, #111827)",
|
|
116
|
+
cursor: "pointer"
|
|
117
|
+
},
|
|
118
|
+
primaryButton: {
|
|
119
|
+
border: "1px solid var(--primary, #0f172a)",
|
|
120
|
+
borderRadius: 6,
|
|
121
|
+
padding: "10px 14px",
|
|
122
|
+
fontSize: 14,
|
|
123
|
+
fontWeight: 700,
|
|
124
|
+
background: "var(--primary, #0f172a)",
|
|
125
|
+
color: "var(--primary-foreground, #fff)",
|
|
126
|
+
cursor: "pointer"
|
|
127
|
+
},
|
|
128
|
+
subtleButton: {
|
|
129
|
+
border: "1px solid var(--border, rgba(15, 23, 42, 0.12))",
|
|
130
|
+
borderRadius: 6,
|
|
131
|
+
padding: "8px 12px",
|
|
132
|
+
fontSize: 13,
|
|
133
|
+
fontWeight: 600,
|
|
134
|
+
background: "var(--secondary, rgba(15, 23, 42, 0.04))",
|
|
135
|
+
color: "var(--secondary-foreground, var(--foreground, #111827))",
|
|
136
|
+
cursor: "pointer"
|
|
137
|
+
},
|
|
138
|
+
warning: {
|
|
139
|
+
border: "1px solid rgba(217, 119, 6, 0.24)",
|
|
140
|
+
background: "rgba(251, 191, 36, 0.12)",
|
|
141
|
+
borderRadius: 8,
|
|
142
|
+
padding: 14,
|
|
143
|
+
fontSize: 14,
|
|
144
|
+
lineHeight: 1.5
|
|
145
|
+
},
|
|
146
|
+
table: {
|
|
147
|
+
width: "100%",
|
|
148
|
+
borderCollapse: "collapse",
|
|
149
|
+
fontSize: 14
|
|
150
|
+
},
|
|
151
|
+
th: {
|
|
152
|
+
textAlign: "left",
|
|
153
|
+
fontSize: 12,
|
|
154
|
+
letterSpacing: 0,
|
|
155
|
+
color: "var(--muted-foreground, rgba(15, 23, 42, 0.62))",
|
|
156
|
+
padding: "10px 12px",
|
|
157
|
+
borderBottom: "1px solid var(--border, rgba(15, 23, 42, 0.1))"
|
|
158
|
+
},
|
|
159
|
+
td: {
|
|
160
|
+
padding: "12px",
|
|
161
|
+
borderBottom: "1px solid var(--border, rgba(15, 23, 42, 0.08))",
|
|
162
|
+
verticalAlign: "top"
|
|
163
|
+
},
|
|
164
|
+
pill: {
|
|
165
|
+
display: "inline-flex",
|
|
166
|
+
alignItems: "center",
|
|
167
|
+
borderRadius: 999,
|
|
168
|
+
padding: "4px 10px",
|
|
169
|
+
background: "var(--secondary, rgba(15, 23, 42, 0.06))",
|
|
170
|
+
color: "var(--secondary-foreground, var(--foreground, #111827))",
|
|
171
|
+
fontSize: 12,
|
|
172
|
+
fontWeight: 600
|
|
173
|
+
},
|
|
174
|
+
monoLink: {
|
|
175
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
176
|
+
fontSize: 12
|
|
177
|
+
},
|
|
178
|
+
helpText: {
|
|
179
|
+
fontSize: 13,
|
|
180
|
+
lineHeight: 1.5,
|
|
181
|
+
color: "var(--muted-foreground, rgba(15, 23, 42, 0.68))"
|
|
182
|
+
},
|
|
183
|
+
statusLine: {
|
|
184
|
+
marginTop: 14,
|
|
185
|
+
paddingTop: 14,
|
|
186
|
+
borderTop: "1px solid var(--border, rgba(15, 23, 42, 0.1))",
|
|
187
|
+
fontSize: 13,
|
|
188
|
+
lineHeight: 1.5,
|
|
189
|
+
color: "var(--muted-foreground, rgba(15, 23, 42, 0.68))"
|
|
190
|
+
},
|
|
191
|
+
sectionHeader: {
|
|
192
|
+
display: "flex",
|
|
193
|
+
justifyContent: "space-between",
|
|
194
|
+
gap: 12,
|
|
195
|
+
alignItems: "flex-start",
|
|
196
|
+
flexWrap: "wrap"
|
|
197
|
+
},
|
|
198
|
+
teamGrid: {
|
|
199
|
+
display: "grid",
|
|
200
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
|
|
201
|
+
gap: 10,
|
|
202
|
+
marginTop: 14
|
|
203
|
+
},
|
|
204
|
+
teamChoice: {
|
|
205
|
+
display: "flex",
|
|
206
|
+
gap: 10,
|
|
207
|
+
alignItems: "flex-start",
|
|
208
|
+
border: "1px solid var(--border, rgba(15, 23, 42, 0.14))",
|
|
209
|
+
borderRadius: 8,
|
|
210
|
+
padding: 12,
|
|
211
|
+
cursor: "pointer",
|
|
212
|
+
background: "var(--background, transparent)"
|
|
213
|
+
},
|
|
214
|
+
checkbox: {
|
|
215
|
+
marginTop: 3
|
|
216
|
+
},
|
|
217
|
+
statusRuleGrid: {
|
|
218
|
+
display: "grid",
|
|
219
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
|
|
220
|
+
gap: 10,
|
|
221
|
+
alignItems: "center"
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
function normalizeConfig(config) {
|
|
225
|
+
const legacyOrganizationMappings = Array.isArray(config?.organizationMappings) ? config.organizationMappings : [];
|
|
226
|
+
const explicitTeamMappings = Array.isArray(config?.teamMappings) ? config.teamMappings : [];
|
|
227
|
+
return {
|
|
228
|
+
apiTokenSecretRef: config?.apiTokenSecretRef ?? "",
|
|
229
|
+
teamMappings: explicitTeamMappings.length > 0 ? explicitTeamMappings : mergeLegacyTeamMappings(legacyOrganizationMappings),
|
|
230
|
+
organizationMappings: legacyOrganizationMappings,
|
|
231
|
+
...config?.fixtureMode === true ? { fixtureMode: true } : {}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function mergeLegacyTeamMappings(mappings) {
|
|
235
|
+
const byTeamId = /* @__PURE__ */ new Map();
|
|
236
|
+
for (const mapping of mappings) {
|
|
237
|
+
for (const team of mapping.teamMappings ?? []) {
|
|
238
|
+
if (!team.teamId || byTeamId.has(team.teamId)) continue;
|
|
239
|
+
byTeamId.set(team.teamId, team);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return [...byTeamId.values()];
|
|
243
|
+
}
|
|
244
|
+
function getOrgPrefix(context) {
|
|
245
|
+
const orgPrefix = typeof context["orgPrefix"] === "string" ? context["orgPrefix"] : null;
|
|
246
|
+
const companyPrefix = typeof context["companyPrefix"] === "string" ? context["companyPrefix"] : null;
|
|
247
|
+
return orgPrefix ?? companyPrefix;
|
|
248
|
+
}
|
|
249
|
+
function getPluginIdFromLocation() {
|
|
250
|
+
if (typeof window === "undefined") return null;
|
|
251
|
+
const match = window.location.pathname.match(/\/instance\/settings\/plugins\/([^/?#]+)/);
|
|
252
|
+
return match?.[1] ?? null;
|
|
253
|
+
}
|
|
254
|
+
async function apiFetch(path, init) {
|
|
255
|
+
const response = await fetch(path, {
|
|
256
|
+
credentials: "same-origin",
|
|
257
|
+
headers: {
|
|
258
|
+
"Content-Type": "application/json",
|
|
259
|
+
...init?.headers ?? {}
|
|
260
|
+
},
|
|
261
|
+
...init
|
|
262
|
+
});
|
|
263
|
+
if (!response.ok) {
|
|
264
|
+
const payload = await response.json().catch(async () => ({ error: await response.text() }));
|
|
265
|
+
const message = typeof payload?.error === "string" ? payload.error : typeof payload?.message === "string" ? payload.message : `Request failed (${response.status})`;
|
|
266
|
+
throw new Error(message);
|
|
267
|
+
}
|
|
268
|
+
return await response.json();
|
|
269
|
+
}
|
|
270
|
+
function formatRudderStatus(status) {
|
|
271
|
+
return status.replaceAll("_", " ");
|
|
272
|
+
}
|
|
273
|
+
function inferRudderStatus(state) {
|
|
274
|
+
const type = (state.type ?? "").toLowerCase();
|
|
275
|
+
const name = state.name.toLowerCase();
|
|
276
|
+
if (type.includes("completed") || name.includes("done") || name.includes("complete")) return "done";
|
|
277
|
+
if (type.includes("cancel") || name.includes("cancel")) return "cancelled";
|
|
278
|
+
if (name.includes("review")) return "in_review";
|
|
279
|
+
if (type.includes("backlog") || type.includes("triage") || name.includes("backlog") || name.includes("triage")) return "backlog";
|
|
280
|
+
if (type.includes("started") || type.includes("unstarted") || name.includes("progress") || name.includes("todo")) return "todo";
|
|
281
|
+
return "backlog";
|
|
282
|
+
}
|
|
283
|
+
function buildTeamMappingsFromCatalog(catalog) {
|
|
284
|
+
return catalog.teams.map((team) => buildTeamMappingFromCatalog(team));
|
|
285
|
+
}
|
|
286
|
+
function buildTeamMappingFromCatalog(team, existing) {
|
|
287
|
+
const existingStatusByStateId = new Map(
|
|
288
|
+
(existing?.stateMappings ?? []).map((state) => [state.linearStateId, state.rudderStatus])
|
|
289
|
+
);
|
|
290
|
+
return {
|
|
291
|
+
teamId: team.id,
|
|
292
|
+
teamName: team.name,
|
|
293
|
+
stateMappings: team.states.map((state) => ({
|
|
294
|
+
linearStateId: state.id,
|
|
295
|
+
linearStateName: state.name,
|
|
296
|
+
rudderStatus: existingStatusByStateId.get(state.id) ?? inferRudderStatus(state)
|
|
297
|
+
}))
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function buildTeamMappingsForSelectedTeams(catalog, selectedTeamIds, existingTeamMappings) {
|
|
301
|
+
const selected = new Set(selectedTeamIds);
|
|
302
|
+
const existingByTeamId = new Map(existingTeamMappings.map((team) => [team.teamId, team]));
|
|
303
|
+
return catalog.teams.filter((team) => selected.has(team.id)).map((team) => buildTeamMappingFromCatalog(team, existingByTeamId.get(team.id)));
|
|
304
|
+
}
|
|
305
|
+
function countMappedStates(teamMappings) {
|
|
306
|
+
return teamMappings?.reduce((sum, team) => sum + team.stateMappings.length, 0) ?? 0;
|
|
307
|
+
}
|
|
308
|
+
function summarizeMapping(teamMappings) {
|
|
309
|
+
if (!teamMappings) return "No Linear workspace loaded yet.";
|
|
310
|
+
const teamCount = teamMappings.length;
|
|
311
|
+
const stateCount = countMappedStates(teamMappings);
|
|
312
|
+
return `${teamCount} team${teamCount === 1 ? "" : "s"} and ${stateCount} workflow state${stateCount === 1 ? "" : "s"} ready.`;
|
|
313
|
+
}
|
|
314
|
+
function isMissingSettingsCatalogHandler(error) {
|
|
315
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
316
|
+
return /No data handler registered/i.test(message) && /settings-catalog/i.test(message);
|
|
317
|
+
}
|
|
318
|
+
function prepareConfigForSubmit(config) {
|
|
319
|
+
const teamMappings = config.teamMappings.map((team) => ({
|
|
320
|
+
teamId: team.teamId.trim(),
|
|
321
|
+
teamName: team.teamName?.trim() || void 0,
|
|
322
|
+
stateMappings: team.stateMappings.map((state) => ({
|
|
323
|
+
linearStateId: state.linearStateId.trim(),
|
|
324
|
+
linearStateName: state.linearStateName?.trim() || void 0,
|
|
325
|
+
rudderStatus: state.rudderStatus
|
|
326
|
+
})).filter((state) => state.linearStateId)
|
|
327
|
+
})).filter((team) => team.teamId);
|
|
328
|
+
return {
|
|
329
|
+
apiTokenSecretRef: config.apiTokenSecretRef?.trim() ?? "",
|
|
330
|
+
...config.fixtureMode === true ? { fixtureMode: true } : {},
|
|
331
|
+
teamMappings,
|
|
332
|
+
// Keep a legacy-compatible shape for already-installed manifests that still
|
|
333
|
+
// require organizationMappings during config validation. New runtime code
|
|
334
|
+
// reads the top-level teamMappings field.
|
|
335
|
+
organizationMappings: [{ orgId: "__global__", teamMappings }]
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function summarizeImportResult(result) {
|
|
339
|
+
const parts = [`Imported ${result.importedCount}`];
|
|
340
|
+
if (result.duplicateCount > 0) parts.push(`${result.duplicateCount} duplicate`);
|
|
341
|
+
if (result.fallbackCount > 0) parts.push(`${result.fallbackCount} fallback`);
|
|
342
|
+
if (result.adjustedCount > 0) parts.push(`${result.adjustedCount} adjusted`);
|
|
343
|
+
return parts.join(" / ");
|
|
344
|
+
}
|
|
345
|
+
function formatRelativeTime(timestamp) {
|
|
346
|
+
if (!timestamp) return "Unknown";
|
|
347
|
+
const date = new Date(timestamp);
|
|
348
|
+
if (Number.isNaN(date.getTime())) return timestamp;
|
|
349
|
+
return date.toLocaleString();
|
|
350
|
+
}
|
|
351
|
+
function issueHref(orgPrefix, issueId) {
|
|
352
|
+
return orgPrefix ? `/${orgPrefix}/issues/${issueId}` : `/issues/${issueId}`;
|
|
353
|
+
}
|
|
354
|
+
function pageHref(orgPrefix, query) {
|
|
355
|
+
if (!orgPrefix) return "/linear";
|
|
356
|
+
const url = new URL(`/${orgPrefix}/linear`, "https://local.invalid");
|
|
357
|
+
if (query) url.searchParams.set("q", query);
|
|
358
|
+
return `${url.pathname}${url.search}`;
|
|
359
|
+
}
|
|
360
|
+
function readLinearPageUrlFilters() {
|
|
361
|
+
if (typeof window === "undefined") {
|
|
362
|
+
return { query: "", projectId: "", teamId: "" };
|
|
363
|
+
}
|
|
364
|
+
const params = new URLSearchParams(window.location.search);
|
|
365
|
+
return {
|
|
366
|
+
query: params.get("q") ?? "",
|
|
367
|
+
projectId: params.get("linearProjectId") ?? "",
|
|
368
|
+
teamId: params.get("linearTeamId") ?? ""
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function useLinearFilters(initialQuery, initialProjectId = "", initialTeamId = "") {
|
|
372
|
+
const [filters, setFilters] = useState({
|
|
373
|
+
teamId: initialTeamId,
|
|
374
|
+
stateId: "",
|
|
375
|
+
projectId: initialProjectId,
|
|
376
|
+
assigneeId: "",
|
|
377
|
+
query: initialQuery
|
|
378
|
+
});
|
|
379
|
+
return [filters, setFilters];
|
|
380
|
+
}
|
|
381
|
+
function LinearPluginPage({ context }) {
|
|
382
|
+
const toast = usePluginToast();
|
|
383
|
+
const importIssues = usePluginAction(ACTION_KEYS.importIssues);
|
|
384
|
+
const orgId = context.orgId ?? "__missing__";
|
|
385
|
+
const orgPrefix = getOrgPrefix(context);
|
|
386
|
+
const urlFilters = readLinearPageUrlFilters();
|
|
387
|
+
const bootstrap = usePluginData(DATA_KEYS.pageBootstrap, { orgId });
|
|
388
|
+
const catalog = usePluginData(DATA_KEYS.catalog, { orgId });
|
|
389
|
+
const [filters, setFilters] = useLinearFilters(urlFilters.query, urlFilters.projectId, urlFilters.teamId);
|
|
390
|
+
const [targetProjectId, setTargetProjectId] = useState("");
|
|
391
|
+
const [afterCursor, setAfterCursor] = useState(null);
|
|
392
|
+
const [cursorHistory, setCursorHistory] = useState([]);
|
|
393
|
+
const [selectedIssueIds, setSelectedIssueIds] = useState([]);
|
|
394
|
+
const [importing, setImporting] = useState(false);
|
|
395
|
+
const issues = usePluginData(DATA_KEYS.issues, {
|
|
396
|
+
orgId,
|
|
397
|
+
limit: LINEAR_PAGE_SIZE,
|
|
398
|
+
after: afterCursor ?? void 0,
|
|
399
|
+
teamId: filters.teamId || void 0,
|
|
400
|
+
stateId: filters.stateId || void 0,
|
|
401
|
+
projectId: filters.projectId || void 0,
|
|
402
|
+
assigneeId: filters.assigneeId || void 0,
|
|
403
|
+
query: filters.query || void 0
|
|
404
|
+
});
|
|
405
|
+
useEffect(() => {
|
|
406
|
+
setFilters((current) => {
|
|
407
|
+
if (current.query === urlFilters.query && current.projectId === urlFilters.projectId && current.teamId === urlFilters.teamId) {
|
|
408
|
+
return current;
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
...current,
|
|
412
|
+
query: urlFilters.query,
|
|
413
|
+
projectId: urlFilters.projectId,
|
|
414
|
+
teamId: urlFilters.teamId
|
|
415
|
+
};
|
|
416
|
+
});
|
|
417
|
+
}, [setFilters, urlFilters.projectId, urlFilters.query, urlFilters.teamId]);
|
|
418
|
+
const stateOptions = useMemo(() => {
|
|
419
|
+
if (!catalog.data?.teams?.length) return [];
|
|
420
|
+
const sourceTeams = filters.teamId ? catalog.data.teams.filter((team) => team.id === filters.teamId) : catalog.data.teams;
|
|
421
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
422
|
+
for (const team of sourceTeams) {
|
|
423
|
+
for (const state of team.states) {
|
|
424
|
+
deduped.set(state.id, { id: state.id, name: state.name });
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return [...deduped.values()];
|
|
428
|
+
}, [catalog.data?.teams, filters.teamId]);
|
|
429
|
+
useEffect(() => {
|
|
430
|
+
setAfterCursor(null);
|
|
431
|
+
setCursorHistory([]);
|
|
432
|
+
setSelectedIssueIds([]);
|
|
433
|
+
}, [filters.teamId, filters.stateId, filters.projectId, filters.assigneeId, filters.query]);
|
|
434
|
+
useEffect(() => {
|
|
435
|
+
const visibleIds = new Set(issues.data?.rows.map((row) => row.id) ?? []);
|
|
436
|
+
setSelectedIssueIds((current) => {
|
|
437
|
+
const next = current.filter((id) => visibleIds.has(id));
|
|
438
|
+
if (next.length === current.length && next.every((id, index) => id === current[index])) {
|
|
439
|
+
return current;
|
|
440
|
+
}
|
|
441
|
+
return next;
|
|
442
|
+
});
|
|
443
|
+
}, [issues.data?.rows]);
|
|
444
|
+
async function handleImport(mode, issueIds) {
|
|
445
|
+
if (!targetProjectId) {
|
|
446
|
+
toast({
|
|
447
|
+
title: "Choose a target project",
|
|
448
|
+
body: "Imports are disabled until a Rudder project is selected.",
|
|
449
|
+
tone: "warn"
|
|
450
|
+
});
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
setImporting(true);
|
|
454
|
+
try {
|
|
455
|
+
const result = await importIssues({
|
|
456
|
+
orgId,
|
|
457
|
+
targetProjectId,
|
|
458
|
+
mode,
|
|
459
|
+
issueIds,
|
|
460
|
+
filters: {
|
|
461
|
+
teamId: filters.teamId || void 0,
|
|
462
|
+
stateId: filters.stateId || void 0,
|
|
463
|
+
projectId: filters.projectId || void 0,
|
|
464
|
+
assigneeId: filters.assigneeId || void 0,
|
|
465
|
+
query: filters.query || void 0
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
toast({
|
|
469
|
+
title: "Linear import complete",
|
|
470
|
+
body: summarizeImportResult(result),
|
|
471
|
+
tone: "success"
|
|
472
|
+
});
|
|
473
|
+
issues.refresh();
|
|
474
|
+
setSelectedIssueIds([]);
|
|
475
|
+
} catch (error) {
|
|
476
|
+
toast({
|
|
477
|
+
title: "Linear import failed",
|
|
478
|
+
body: error instanceof Error ? error.message : String(error),
|
|
479
|
+
tone: "error"
|
|
480
|
+
});
|
|
481
|
+
} finally {
|
|
482
|
+
setImporting(false);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return /* @__PURE__ */ jsxs("div", { style: layoutStyles.shell, children: [
|
|
486
|
+
/* @__PURE__ */ jsxs("section", { style: layoutStyles.card, children: [
|
|
487
|
+
/* @__PURE__ */ jsx("h1", { style: layoutStyles.title, children: "Linear intake" }),
|
|
488
|
+
/* @__PURE__ */ jsx("p", { style: layoutStyles.subtitle, children: "Import Linear issues into a chosen Rudder project. This page is the bulk workspace; the issue tab is the linked detail view." })
|
|
489
|
+
] }),
|
|
490
|
+
bootstrap.loading ? /* @__PURE__ */ jsx("section", { style: layoutStyles.card, children: "Loading Linear import context\u2026" }) : !bootstrap.data?.configured ? /* @__PURE__ */ jsxs("section", { style: { ...layoutStyles.card, ...layoutStyles.warning }, children: [
|
|
491
|
+
/* @__PURE__ */ jsx("strong", { children: "Linear is not configured yet." }),
|
|
492
|
+
/* @__PURE__ */ jsx("div", { style: { marginTop: 8 }, children: bootstrap.data?.message ?? "Connect Linear and choose teams in plugin settings." }),
|
|
493
|
+
/* @__PURE__ */ jsx("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ jsx("a", { href: "/instance/settings/plugins", style: layoutStyles.monoLink, children: "Open plugin settings" }) })
|
|
494
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
495
|
+
/* @__PURE__ */ jsx("section", { style: layoutStyles.card, children: /* @__PURE__ */ jsxs("div", { style: layoutStyles.row, children: [
|
|
496
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.field, children: [
|
|
497
|
+
/* @__PURE__ */ jsx("label", { style: layoutStyles.label, htmlFor: "target-project", children: "Target Rudder project" }),
|
|
498
|
+
/* @__PURE__ */ jsxs(
|
|
499
|
+
"select",
|
|
500
|
+
{
|
|
501
|
+
id: "target-project",
|
|
502
|
+
"data-testid": "linear-target-project",
|
|
503
|
+
style: layoutStyles.select,
|
|
504
|
+
value: targetProjectId,
|
|
505
|
+
onChange: (event) => setTargetProjectId(event.target.value),
|
|
506
|
+
children: [
|
|
507
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Choose a project" }),
|
|
508
|
+
(bootstrap.data?.projects ?? []).map((project) => /* @__PURE__ */ jsx("option", { value: project.id, children: project.name }, project.id))
|
|
509
|
+
]
|
|
510
|
+
}
|
|
511
|
+
)
|
|
512
|
+
] }),
|
|
513
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.field, children: [
|
|
514
|
+
/* @__PURE__ */ jsx("label", { style: layoutStyles.label, htmlFor: "linear-team-filter", children: "Linear team" }),
|
|
515
|
+
/* @__PURE__ */ jsxs(
|
|
516
|
+
"select",
|
|
517
|
+
{
|
|
518
|
+
id: "linear-team-filter",
|
|
519
|
+
style: layoutStyles.select,
|
|
520
|
+
value: filters.teamId,
|
|
521
|
+
onChange: (event) => setFilters((current) => ({ ...current, teamId: event.target.value, stateId: "" })),
|
|
522
|
+
children: [
|
|
523
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "All allowed teams" }),
|
|
524
|
+
(catalog.data?.teams ?? []).map((team) => /* @__PURE__ */ jsx("option", { value: team.id, children: team.name }, team.id))
|
|
525
|
+
]
|
|
526
|
+
}
|
|
527
|
+
)
|
|
528
|
+
] }),
|
|
529
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.field, children: [
|
|
530
|
+
/* @__PURE__ */ jsx("label", { style: layoutStyles.label, htmlFor: "linear-state-filter", children: "Workflow state" }),
|
|
531
|
+
/* @__PURE__ */ jsxs(
|
|
532
|
+
"select",
|
|
533
|
+
{
|
|
534
|
+
id: "linear-state-filter",
|
|
535
|
+
style: layoutStyles.select,
|
|
536
|
+
value: filters.stateId,
|
|
537
|
+
onChange: (event) => setFilters((current) => ({ ...current, stateId: event.target.value })),
|
|
538
|
+
children: [
|
|
539
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "All states" }),
|
|
540
|
+
stateOptions.map((state) => /* @__PURE__ */ jsx("option", { value: state.id, children: state.name }, state.id))
|
|
541
|
+
]
|
|
542
|
+
}
|
|
543
|
+
)
|
|
544
|
+
] }),
|
|
545
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.field, children: [
|
|
546
|
+
/* @__PURE__ */ jsx("label", { style: layoutStyles.label, htmlFor: "linear-project-filter", children: "Linear project" }),
|
|
547
|
+
/* @__PURE__ */ jsxs(
|
|
548
|
+
"select",
|
|
549
|
+
{
|
|
550
|
+
id: "linear-project-filter",
|
|
551
|
+
style: layoutStyles.select,
|
|
552
|
+
value: filters.projectId,
|
|
553
|
+
onChange: (event) => setFilters((current) => ({ ...current, projectId: event.target.value })),
|
|
554
|
+
children: [
|
|
555
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "All projects" }),
|
|
556
|
+
(catalog.data?.projects ?? []).map((project) => /* @__PURE__ */ jsx("option", { value: project.id, children: project.name }, project.id))
|
|
557
|
+
]
|
|
558
|
+
}
|
|
559
|
+
)
|
|
560
|
+
] }),
|
|
561
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.field, children: [
|
|
562
|
+
/* @__PURE__ */ jsx("label", { style: layoutStyles.label, htmlFor: "linear-assignee-filter", children: "Assignee" }),
|
|
563
|
+
/* @__PURE__ */ jsxs(
|
|
564
|
+
"select",
|
|
565
|
+
{
|
|
566
|
+
id: "linear-assignee-filter",
|
|
567
|
+
style: layoutStyles.select,
|
|
568
|
+
value: filters.assigneeId,
|
|
569
|
+
onChange: (event) => setFilters((current) => ({ ...current, assigneeId: event.target.value })),
|
|
570
|
+
children: [
|
|
571
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Anyone" }),
|
|
572
|
+
(catalog.data?.users ?? []).map((user) => /* @__PURE__ */ jsx("option", { value: user.id, children: user.name }, user.id))
|
|
573
|
+
]
|
|
574
|
+
}
|
|
575
|
+
)
|
|
576
|
+
] }),
|
|
577
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.field, children: [
|
|
578
|
+
/* @__PURE__ */ jsx("label", { style: layoutStyles.label, htmlFor: "linear-query-filter", children: "Search" }),
|
|
579
|
+
/* @__PURE__ */ jsx(
|
|
580
|
+
"input",
|
|
581
|
+
{
|
|
582
|
+
id: "linear-query-filter",
|
|
583
|
+
style: layoutStyles.input,
|
|
584
|
+
value: filters.query,
|
|
585
|
+
onChange: (event) => setFilters((current) => ({ ...current, query: event.target.value })),
|
|
586
|
+
placeholder: "Identifier, title, or description"
|
|
587
|
+
}
|
|
588
|
+
)
|
|
589
|
+
] })
|
|
590
|
+
] }) }),
|
|
591
|
+
/* @__PURE__ */ jsxs("section", { style: layoutStyles.card, children: [
|
|
592
|
+
/* @__PURE__ */ jsxs("div", { style: { ...layoutStyles.row, justifyContent: "space-between" }, children: [
|
|
593
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.row, children: [
|
|
594
|
+
/* @__PURE__ */ jsx(
|
|
595
|
+
"button",
|
|
596
|
+
{
|
|
597
|
+
type: "button",
|
|
598
|
+
style: layoutStyles.button,
|
|
599
|
+
onClick: () => {
|
|
600
|
+
const rows = issues.data?.rows ?? [];
|
|
601
|
+
const selectableIds = rows.filter((row) => !row.imported).map((row) => row.id);
|
|
602
|
+
setSelectedIssueIds(selectableIds);
|
|
603
|
+
},
|
|
604
|
+
disabled: issues.loading,
|
|
605
|
+
children: "Select current page"
|
|
606
|
+
}
|
|
607
|
+
),
|
|
608
|
+
/* @__PURE__ */ jsx(
|
|
609
|
+
"button",
|
|
610
|
+
{
|
|
611
|
+
type: "button",
|
|
612
|
+
style: layoutStyles.button,
|
|
613
|
+
onClick: () => setSelectedIssueIds([]),
|
|
614
|
+
disabled: selectedIssueIds.length === 0,
|
|
615
|
+
children: "Clear selection"
|
|
616
|
+
}
|
|
617
|
+
)
|
|
618
|
+
] }),
|
|
619
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.row, children: [
|
|
620
|
+
/* @__PURE__ */ jsx(
|
|
621
|
+
"button",
|
|
622
|
+
{
|
|
623
|
+
type: "button",
|
|
624
|
+
style: layoutStyles.button,
|
|
625
|
+
disabled: !targetProjectId || selectedIssueIds.length === 0 || importing,
|
|
626
|
+
onClick: () => void handleImport("selected", selectedIssueIds),
|
|
627
|
+
children: "Import selected"
|
|
628
|
+
}
|
|
629
|
+
),
|
|
630
|
+
/* @__PURE__ */ jsx(
|
|
631
|
+
"button",
|
|
632
|
+
{
|
|
633
|
+
type: "button",
|
|
634
|
+
style: layoutStyles.primaryButton,
|
|
635
|
+
"data-testid": "linear-import-all",
|
|
636
|
+
disabled: !targetProjectId || importing,
|
|
637
|
+
onClick: () => void handleImport("allMatching"),
|
|
638
|
+
title: `Imports up to ${LINEAR_IMPORT_ALL_LIMIT} matching issues.`,
|
|
639
|
+
children: "Import all matching"
|
|
640
|
+
}
|
|
641
|
+
)
|
|
642
|
+
] })
|
|
643
|
+
] }),
|
|
644
|
+
!targetProjectId && /* @__PURE__ */ jsx("div", { style: { marginTop: 12, ...layoutStyles.warning }, children: "Choose a target Rudder project to enable per-row, selected, or all-matching import actions." })
|
|
645
|
+
] }),
|
|
646
|
+
/* @__PURE__ */ jsx("section", { style: layoutStyles.card, children: issues.loading ? /* @__PURE__ */ jsx("div", { children: "Loading Linear issues\u2026" }) : issues.error ? /* @__PURE__ */ jsx("div", { style: layoutStyles.warning, children: issues.error.message }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
647
|
+
/* @__PURE__ */ jsxs("table", { style: layoutStyles.table, children: [
|
|
648
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
649
|
+
/* @__PURE__ */ jsx("th", { style: layoutStyles.th }),
|
|
650
|
+
/* @__PURE__ */ jsx("th", { style: layoutStyles.th, children: "Issue" }),
|
|
651
|
+
/* @__PURE__ */ jsx("th", { style: layoutStyles.th, children: "State" }),
|
|
652
|
+
/* @__PURE__ */ jsx("th", { style: layoutStyles.th, children: "Project" }),
|
|
653
|
+
/* @__PURE__ */ jsx("th", { style: layoutStyles.th, children: "Assignee" }),
|
|
654
|
+
/* @__PURE__ */ jsx("th", { style: layoutStyles.th, children: "Status" }),
|
|
655
|
+
/* @__PURE__ */ jsx("th", { style: layoutStyles.th, children: "Action" })
|
|
656
|
+
] }) }),
|
|
657
|
+
/* @__PURE__ */ jsx("tbody", { children: (issues.data?.rows ?? []).map((row) => {
|
|
658
|
+
const checked = selectedIssueIds.includes(row.id);
|
|
659
|
+
const sameOrgLink = row.imported && (!row.importedOrgId || row.importedOrgId === context.orgId);
|
|
660
|
+
return /* @__PURE__ */ jsxs("tr", { children: [
|
|
661
|
+
/* @__PURE__ */ jsx("td", { style: layoutStyles.td, children: /* @__PURE__ */ jsx(
|
|
662
|
+
"input",
|
|
663
|
+
{
|
|
664
|
+
type: "checkbox",
|
|
665
|
+
checked,
|
|
666
|
+
disabled: row.imported,
|
|
667
|
+
onChange: (event) => {
|
|
668
|
+
setSelectedIssueIds((current) => {
|
|
669
|
+
if (event.target.checked) return [...current, row.id];
|
|
670
|
+
return current.filter((id) => id !== row.id);
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
) }),
|
|
675
|
+
/* @__PURE__ */ jsx("td", { style: layoutStyles.td, children: /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 6 }, children: [
|
|
676
|
+
/* @__PURE__ */ jsxs("a", { href: row.url, target: "_blank", rel: "noreferrer", children: [
|
|
677
|
+
/* @__PURE__ */ jsx("strong", { children: row.identifier }),
|
|
678
|
+
" ",
|
|
679
|
+
row.title
|
|
680
|
+
] }),
|
|
681
|
+
/* @__PURE__ */ jsx("span", { style: layoutStyles.pill, children: row.team.name })
|
|
682
|
+
] }) }),
|
|
683
|
+
/* @__PURE__ */ jsx("td", { style: layoutStyles.td, children: row.state.name }),
|
|
684
|
+
/* @__PURE__ */ jsx("td", { style: layoutStyles.td, children: row.project?.name ?? "None" }),
|
|
685
|
+
/* @__PURE__ */ jsx("td", { style: layoutStyles.td, children: row.assignee?.name ?? "Unassigned" }),
|
|
686
|
+
/* @__PURE__ */ jsx("td", { style: layoutStyles.td, children: row.imported ? /* @__PURE__ */ jsx("span", { "data-testid": `linear-imported-${row.id}`, style: layoutStyles.pill, children: "Imported" }) : /* @__PURE__ */ jsx("span", { style: layoutStyles.pill, children: "Ready" }) }),
|
|
687
|
+
/* @__PURE__ */ jsx("td", { style: layoutStyles.td, children: row.imported ? sameOrgLink && row.importedRudderIssueId ? /* @__PURE__ */ jsx("a", { href: issueHref(orgPrefix, row.importedRudderIssueId), children: "Open Rudder issue" }) : /* @__PURE__ */ jsx("span", { children: "Imported elsewhere" }) : /* @__PURE__ */ jsx(
|
|
688
|
+
"button",
|
|
689
|
+
{
|
|
690
|
+
type: "button",
|
|
691
|
+
style: layoutStyles.subtleButton,
|
|
692
|
+
disabled: !targetProjectId || importing,
|
|
693
|
+
onClick: () => void handleImport("single", [row.id]),
|
|
694
|
+
children: "Import"
|
|
695
|
+
}
|
|
696
|
+
) })
|
|
697
|
+
] }, row.id);
|
|
698
|
+
}) })
|
|
699
|
+
] }),
|
|
700
|
+
(issues.data?.rows.length ?? 0) === 0 && /* @__PURE__ */ jsx("div", { style: { marginTop: 16, color: "rgba(15, 23, 42, 0.68)" }, children: "No Linear issues matched the current filters." }),
|
|
701
|
+
/* @__PURE__ */ jsxs("div", { style: { ...layoutStyles.row, justifyContent: "space-between", marginTop: 16 }, children: [
|
|
702
|
+
/* @__PURE__ */ jsxs("span", { style: { fontSize: 13, color: "rgba(15, 23, 42, 0.68)" }, children: [
|
|
703
|
+
"Showing ",
|
|
704
|
+
issues.data?.totalShown ?? 0,
|
|
705
|
+
" issue(s)."
|
|
706
|
+
] }),
|
|
707
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.row, children: [
|
|
708
|
+
/* @__PURE__ */ jsx(
|
|
709
|
+
"button",
|
|
710
|
+
{
|
|
711
|
+
type: "button",
|
|
712
|
+
style: layoutStyles.button,
|
|
713
|
+
disabled: cursorHistory.length === 0 || importing,
|
|
714
|
+
onClick: () => {
|
|
715
|
+
setCursorHistory((current) => {
|
|
716
|
+
const nextHistory = [...current];
|
|
717
|
+
const previousCursor = nextHistory.pop() ?? null;
|
|
718
|
+
setAfterCursor(previousCursor);
|
|
719
|
+
return nextHistory;
|
|
720
|
+
});
|
|
721
|
+
},
|
|
722
|
+
children: "Previous"
|
|
723
|
+
}
|
|
724
|
+
),
|
|
725
|
+
/* @__PURE__ */ jsx(
|
|
726
|
+
"button",
|
|
727
|
+
{
|
|
728
|
+
type: "button",
|
|
729
|
+
style: layoutStyles.button,
|
|
730
|
+
disabled: !issues.data?.hasNextPage || importing,
|
|
731
|
+
onClick: () => {
|
|
732
|
+
if (!issues.data?.endCursor) return;
|
|
733
|
+
setCursorHistory((current) => [...current, afterCursor ?? ""]);
|
|
734
|
+
setAfterCursor(issues.data.endCursor);
|
|
735
|
+
},
|
|
736
|
+
children: "Next"
|
|
737
|
+
}
|
|
738
|
+
)
|
|
739
|
+
] })
|
|
740
|
+
] })
|
|
741
|
+
] }) })
|
|
742
|
+
] })
|
|
743
|
+
] });
|
|
744
|
+
}
|
|
745
|
+
function LinearIssueTab({ context }) {
|
|
746
|
+
const orgId = context.orgId ?? "__missing__";
|
|
747
|
+
const orgPrefix = getOrgPrefix(context);
|
|
748
|
+
const data = usePluginData(DATA_KEYS.issueLink, {
|
|
749
|
+
orgId,
|
|
750
|
+
issueId: context.entityId
|
|
751
|
+
});
|
|
752
|
+
if (data.loading) {
|
|
753
|
+
return /* @__PURE__ */ jsx("div", { style: layoutStyles.card, children: "Loading Linear issue details\u2026" });
|
|
754
|
+
}
|
|
755
|
+
if (data.error) {
|
|
756
|
+
return /* @__PURE__ */ jsx("div", { style: { ...layoutStyles.card, ...layoutStyles.warning }, children: data.error.message });
|
|
757
|
+
}
|
|
758
|
+
if (!data.data || !data.data.linked) {
|
|
759
|
+
return /* @__PURE__ */ jsxs("div", { style: layoutStyles.card, children: [
|
|
760
|
+
/* @__PURE__ */ jsx("h2", { style: { marginTop: 0 }, children: "No linked Linear issue" }),
|
|
761
|
+
/* @__PURE__ */ jsx("p", { style: layoutStyles.subtitle, children: "This Rudder issue has not been imported from Linear yet." }),
|
|
762
|
+
/* @__PURE__ */ jsx("a", { href: pageHref(orgPrefix, data.data?.searchQuery ?? ""), children: "Open Linear intake with this issue title as the search query" })
|
|
763
|
+
] });
|
|
764
|
+
}
|
|
765
|
+
const latest = data.data.latestIssue;
|
|
766
|
+
const link = data.data.link;
|
|
767
|
+
return /* @__PURE__ */ jsxs("div", { style: layoutStyles.shell, children: [
|
|
768
|
+
/* @__PURE__ */ jsxs("section", { style: layoutStyles.card, children: [
|
|
769
|
+
/* @__PURE__ */ jsx("h2", { style: { marginTop: 0 }, children: "Linked Linear issue" }),
|
|
770
|
+
/* @__PURE__ */ jsxs("p", { style: layoutStyles.subtitle, children: [
|
|
771
|
+
link.linearIdentifier,
|
|
772
|
+
" maps to this Rudder issue."
|
|
773
|
+
] }),
|
|
774
|
+
/* @__PURE__ */ jsxs("div", { style: { ...layoutStyles.row, marginTop: 12 }, children: [
|
|
775
|
+
/* @__PURE__ */ jsx("a", { href: link.linearUrl, target: "_blank", rel: "noreferrer", children: "Open in Linear" }),
|
|
776
|
+
/* @__PURE__ */ jsx("span", { style: layoutStyles.pill, children: latest?.team.name ?? link.teamName }),
|
|
777
|
+
/* @__PURE__ */ jsx("span", { style: layoutStyles.pill, children: latest?.state.name ?? link.stateName }),
|
|
778
|
+
latest?.project?.name ? /* @__PURE__ */ jsx("span", { style: layoutStyles.pill, children: latest.project.name }) : null
|
|
779
|
+
] })
|
|
780
|
+
] }),
|
|
781
|
+
data.data.staleReason ? /* @__PURE__ */ jsx("section", { style: { ...layoutStyles.card, ...layoutStyles.warning }, children: data.data.staleReason }) : null,
|
|
782
|
+
/* @__PURE__ */ jsxs("section", { style: layoutStyles.card, children: [
|
|
783
|
+
/* @__PURE__ */ jsxs("h3", { style: { marginTop: 0 }, children: [
|
|
784
|
+
latest?.identifier ?? link.linearIdentifier,
|
|
785
|
+
" ",
|
|
786
|
+
latest?.title ?? link.linearTitle
|
|
787
|
+
] }),
|
|
788
|
+
/* @__PURE__ */ jsxs("div", { style: { ...layoutStyles.row, marginBottom: 12 }, children: [
|
|
789
|
+
/* @__PURE__ */ jsxs("span", { style: layoutStyles.pill, children: [
|
|
790
|
+
"Updated ",
|
|
791
|
+
formatRelativeTime(latest?.updatedAt ?? link.updatedAt)
|
|
792
|
+
] }),
|
|
793
|
+
/* @__PURE__ */ jsxs("span", { style: layoutStyles.pill, children: [
|
|
794
|
+
"Imported ",
|
|
795
|
+
formatRelativeTime(link.importedAt)
|
|
796
|
+
] }),
|
|
797
|
+
/* @__PURE__ */ jsx("span", { style: layoutStyles.pill, children: latest?.assignee?.name ?? "Unassigned" })
|
|
798
|
+
] }),
|
|
799
|
+
/* @__PURE__ */ jsx("div", { style: { whiteSpace: "pre-wrap", lineHeight: 1.6 }, children: latest?.description?.trim() || "This Linear issue has no description." })
|
|
800
|
+
] })
|
|
801
|
+
] });
|
|
802
|
+
}
|
|
803
|
+
function LinearPluginSettingsPage(_props) {
|
|
804
|
+
const toast = usePluginToast();
|
|
805
|
+
const bootstrap = usePluginData(DATA_KEYS.settingsBootstrap);
|
|
806
|
+
const [draft, setDraft] = useState(normalizeConfig(null));
|
|
807
|
+
const [tokenInput, setTokenInput] = useState("");
|
|
808
|
+
const [settingsCatalog, setSettingsCatalog] = useState(null);
|
|
809
|
+
const [catalogLoading, setCatalogLoading] = useState(false);
|
|
810
|
+
const [catalogError, setCatalogError] = useState(null);
|
|
811
|
+
const [connecting, setConnecting] = useState(false);
|
|
812
|
+
const [saving, setSaving] = useState(false);
|
|
813
|
+
const pluginId = getPluginIdFromLocation();
|
|
814
|
+
useEffect(() => {
|
|
815
|
+
if (!bootstrap.data) return;
|
|
816
|
+
const nextDraft = normalizeConfig(bootstrap.data.config);
|
|
817
|
+
setDraft(nextDraft);
|
|
818
|
+
}, [bootstrap.data]);
|
|
819
|
+
useEffect(() => {
|
|
820
|
+
if (!pluginId || !draft.apiTokenSecretRef) {
|
|
821
|
+
setSettingsCatalog(null);
|
|
822
|
+
setCatalogError(null);
|
|
823
|
+
setCatalogLoading(false);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
let cancelled = false;
|
|
827
|
+
setCatalogLoading(true);
|
|
828
|
+
setCatalogError(null);
|
|
829
|
+
void fetchSettingsCatalog().then((catalog) => {
|
|
830
|
+
if (cancelled) return;
|
|
831
|
+
setSettingsCatalog(catalog);
|
|
832
|
+
setDraft((current) => {
|
|
833
|
+
const normalized = normalizeConfig(current);
|
|
834
|
+
const selectedTeamIds2 = normalized.teamMappings.map((team) => team.teamId).filter(Boolean);
|
|
835
|
+
const nextTeamMappings = selectedTeamIds2.length > 0 ? buildTeamMappingsForSelectedTeams(catalog, selectedTeamIds2, normalized.teamMappings) : buildTeamMappingsFromCatalog(catalog);
|
|
836
|
+
return {
|
|
837
|
+
...normalized,
|
|
838
|
+
teamMappings: nextTeamMappings
|
|
839
|
+
};
|
|
840
|
+
});
|
|
841
|
+
}).catch((error) => {
|
|
842
|
+
if (cancelled) return;
|
|
843
|
+
setSettingsCatalog(null);
|
|
844
|
+
setCatalogError(error instanceof Error ? error.message : String(error));
|
|
845
|
+
}).finally(() => {
|
|
846
|
+
if (!cancelled) setCatalogLoading(false);
|
|
847
|
+
});
|
|
848
|
+
return () => {
|
|
849
|
+
cancelled = true;
|
|
850
|
+
};
|
|
851
|
+
}, [draft.apiTokenSecretRef, pluginId]);
|
|
852
|
+
const selectedTeamIds = draft.teamMappings.map((team) => team.teamId).filter(Boolean);
|
|
853
|
+
const selectedTeamIdSet = new Set(selectedTeamIds);
|
|
854
|
+
const selectedTeamCount = selectedTeamIds.length;
|
|
855
|
+
const catalogTeamCount = settingsCatalog?.teams.length ?? 0;
|
|
856
|
+
async function savePluginConfig(config) {
|
|
857
|
+
if (!pluginId) throw new Error("Unable to resolve plugin id");
|
|
858
|
+
return await apiFetch(`/api/plugins/${encodeURIComponent(pluginId)}/config`, {
|
|
859
|
+
method: "POST",
|
|
860
|
+
body: JSON.stringify({ configJson: prepareConfigForSubmit(config) })
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
async function fetchPluginData(key, params = {}, orgId) {
|
|
864
|
+
if (!pluginId) throw new Error("Unable to resolve plugin id");
|
|
865
|
+
const payload = await apiFetch(`/api/plugins/${encodeURIComponent(pluginId)}/data/${key}`, {
|
|
866
|
+
method: "POST",
|
|
867
|
+
body: JSON.stringify({
|
|
868
|
+
...orgId ? { orgId } : {},
|
|
869
|
+
params: {
|
|
870
|
+
...params,
|
|
871
|
+
...orgId ? { orgId } : {}
|
|
872
|
+
}
|
|
873
|
+
})
|
|
874
|
+
});
|
|
875
|
+
return payload.data;
|
|
876
|
+
}
|
|
877
|
+
async function fetchSettingsCatalogWithRetry() {
|
|
878
|
+
let lastError = null;
|
|
879
|
+
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
880
|
+
try {
|
|
881
|
+
return await fetchPluginData(DATA_KEYS.settingsCatalog);
|
|
882
|
+
} catch (error) {
|
|
883
|
+
if (isMissingSettingsCatalogHandler(error)) throw error;
|
|
884
|
+
lastError = error;
|
|
885
|
+
await new Promise((resolve) => window.setTimeout(resolve, 350 + attempt * 250));
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
889
|
+
}
|
|
890
|
+
async function refreshPluginRuntime() {
|
|
891
|
+
if (!pluginId) throw new Error("Unable to resolve plugin id");
|
|
892
|
+
await apiFetch(`/api/plugins/${encodeURIComponent(pluginId)}/disable`, {
|
|
893
|
+
method: "POST",
|
|
894
|
+
body: JSON.stringify({ reason: "Refresh Linear plugin after settings update" })
|
|
895
|
+
});
|
|
896
|
+
await apiFetch(`/api/plugins/${encodeURIComponent(pluginId)}/enable`, {
|
|
897
|
+
method: "POST",
|
|
898
|
+
body: JSON.stringify({})
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
async function fetchSettingsCatalog() {
|
|
902
|
+
try {
|
|
903
|
+
return await fetchSettingsCatalogWithRetry();
|
|
904
|
+
} catch (error) {
|
|
905
|
+
if (!isMissingSettingsCatalogHandler(error)) throw error;
|
|
906
|
+
await refreshPluginRuntime();
|
|
907
|
+
return await fetchSettingsCatalogWithRetry();
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
function toggleTeam(teamId) {
|
|
911
|
+
if (!settingsCatalog) return;
|
|
912
|
+
setDraft((current) => {
|
|
913
|
+
const normalized = normalizeConfig(current);
|
|
914
|
+
const nextTeamIds = new Set(normalized.teamMappings.map((team) => team.teamId).filter(Boolean));
|
|
915
|
+
if (nextTeamIds.has(teamId)) {
|
|
916
|
+
nextTeamIds.delete(teamId);
|
|
917
|
+
} else {
|
|
918
|
+
nextTeamIds.add(teamId);
|
|
919
|
+
}
|
|
920
|
+
return {
|
|
921
|
+
...normalized,
|
|
922
|
+
teamMappings: buildTeamMappingsForSelectedTeams(settingsCatalog, [...nextTeamIds], normalized.teamMappings)
|
|
923
|
+
};
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
function selectAllTeams() {
|
|
927
|
+
if (!settingsCatalog) return;
|
|
928
|
+
setDraft((current) => {
|
|
929
|
+
const normalized = normalizeConfig(current);
|
|
930
|
+
return {
|
|
931
|
+
...normalized,
|
|
932
|
+
teamMappings: buildTeamMappingsForSelectedTeams(
|
|
933
|
+
settingsCatalog,
|
|
934
|
+
settingsCatalog.teams.map((team) => team.id),
|
|
935
|
+
normalized.teamMappings
|
|
936
|
+
)
|
|
937
|
+
};
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
function setStatusRule(teamId, stateId, rudderStatus) {
|
|
941
|
+
setDraft((current) => ({
|
|
942
|
+
...current,
|
|
943
|
+
teamMappings: current.teamMappings.map((team) => {
|
|
944
|
+
if (team.teamId !== teamId) return team;
|
|
945
|
+
return {
|
|
946
|
+
...team,
|
|
947
|
+
stateMappings: team.stateMappings.map(
|
|
948
|
+
(state) => state.linearStateId === stateId ? { ...state, rudderStatus } : state
|
|
949
|
+
)
|
|
950
|
+
};
|
|
951
|
+
})
|
|
952
|
+
}));
|
|
953
|
+
}
|
|
954
|
+
async function connectLinear() {
|
|
955
|
+
if (!pluginId) {
|
|
956
|
+
toast({ title: "Unable to resolve plugin id", tone: "error" });
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
setConnecting(true);
|
|
960
|
+
try {
|
|
961
|
+
let apiTokenSecretRef = draft.apiTokenSecretRef?.trim() ?? "";
|
|
962
|
+
const trimmedToken = tokenInput.trim();
|
|
963
|
+
if (trimmedToken) {
|
|
964
|
+
const secretOrgId = bootstrap.data?.organizations[0]?.id ?? "";
|
|
965
|
+
if (!secretOrgId) {
|
|
966
|
+
toast({
|
|
967
|
+
title: "Create a Rudder organization first",
|
|
968
|
+
body: "Rudder needs one organization to store the Linear token reference.",
|
|
969
|
+
tone: "warn"
|
|
970
|
+
});
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
const secret = await apiFetch(`/api/orgs/${encodeURIComponent(secretOrgId)}/secrets`, {
|
|
974
|
+
method: "POST",
|
|
975
|
+
body: JSON.stringify({
|
|
976
|
+
name: "Linear token",
|
|
977
|
+
value: trimmedToken,
|
|
978
|
+
description: "Used by the Linear plugin to read issues across Rudder organizations."
|
|
979
|
+
})
|
|
980
|
+
});
|
|
981
|
+
apiTokenSecretRef = secret.id;
|
|
982
|
+
}
|
|
983
|
+
if (!apiTokenSecretRef) {
|
|
984
|
+
toast({
|
|
985
|
+
title: "Paste a Linear token",
|
|
986
|
+
body: "Create one in Linear, paste it here, then connect.",
|
|
987
|
+
tone: "warn"
|
|
988
|
+
});
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const seedConfig = {
|
|
992
|
+
...draft,
|
|
993
|
+
apiTokenSecretRef
|
|
994
|
+
};
|
|
995
|
+
await savePluginConfig(seedConfig);
|
|
996
|
+
const catalog = await fetchSettingsCatalog();
|
|
997
|
+
if (catalog.teams.length === 0) {
|
|
998
|
+
throw new Error("Linear returned no teams for this token.");
|
|
999
|
+
}
|
|
1000
|
+
const generatedTeamMappings = buildTeamMappingsFromCatalog(catalog);
|
|
1001
|
+
const nextConfig = prepareConfigForSubmit({
|
|
1002
|
+
...seedConfig,
|
|
1003
|
+
teamMappings: generatedTeamMappings
|
|
1004
|
+
});
|
|
1005
|
+
await savePluginConfig(nextConfig);
|
|
1006
|
+
setSettingsCatalog(catalog);
|
|
1007
|
+
setCatalogError(null);
|
|
1008
|
+
setDraft(nextConfig);
|
|
1009
|
+
setTokenInput("");
|
|
1010
|
+
bootstrap.refresh();
|
|
1011
|
+
toast({
|
|
1012
|
+
title: "Linear is ready",
|
|
1013
|
+
body: summarizeMapping(generatedTeamMappings),
|
|
1014
|
+
tone: "success"
|
|
1015
|
+
});
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
toast({
|
|
1018
|
+
title: "Linear connection failed",
|
|
1019
|
+
body: error instanceof Error ? error.message : String(error),
|
|
1020
|
+
tone: "error"
|
|
1021
|
+
});
|
|
1022
|
+
} finally {
|
|
1023
|
+
setConnecting(false);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
async function saveConfig() {
|
|
1027
|
+
if (!pluginId) {
|
|
1028
|
+
toast({ title: "Unable to resolve plugin id", tone: "error" });
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
if (draft.apiTokenSecretRef && settingsCatalog && selectedTeamCount === 0) {
|
|
1032
|
+
toast({
|
|
1033
|
+
title: "Choose at least one Linear team",
|
|
1034
|
+
body: "The import page needs one or more teams to show Linear issues.",
|
|
1035
|
+
tone: "warn"
|
|
1036
|
+
});
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
setSaving(true);
|
|
1040
|
+
try {
|
|
1041
|
+
await savePluginConfig(draft);
|
|
1042
|
+
bootstrap.refresh();
|
|
1043
|
+
toast({
|
|
1044
|
+
title: "Linear settings saved",
|
|
1045
|
+
tone: "success"
|
|
1046
|
+
});
|
|
1047
|
+
} catch (error) {
|
|
1048
|
+
toast({
|
|
1049
|
+
title: "Failed to save Linear settings",
|
|
1050
|
+
body: error instanceof Error ? error.message : String(error),
|
|
1051
|
+
tone: "error"
|
|
1052
|
+
});
|
|
1053
|
+
} finally {
|
|
1054
|
+
setSaving(false);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
function connectionStatusText() {
|
|
1058
|
+
if (bootstrap.loading) return "Loading plugin settings\u2026";
|
|
1059
|
+
if (catalogLoading) return "Reading teams and workflow states from Linear\u2026";
|
|
1060
|
+
if (catalogError) return `Linear could not be loaded: ${catalogError}`;
|
|
1061
|
+
if (settingsCatalog) {
|
|
1062
|
+
return `Connected. ${catalogTeamCount} Linear team${catalogTeamCount === 1 ? "" : "s"} found; ${selectedTeamCount} selected for import.`;
|
|
1063
|
+
}
|
|
1064
|
+
if (draft.apiTokenSecretRef) return "Token saved. Refresh from Linear to load teams.";
|
|
1065
|
+
return "Paste a Linear token to connect this instance.";
|
|
1066
|
+
}
|
|
1067
|
+
return /* @__PURE__ */ jsxs("div", { style: layoutStyles.shell, children: [
|
|
1068
|
+
/* @__PURE__ */ jsxs("section", { style: layoutStyles.card, children: [
|
|
1069
|
+
/* @__PURE__ */ jsx("h2", { style: { marginTop: 0 }, children: "Linear" }),
|
|
1070
|
+
/* @__PURE__ */ jsx("p", { style: layoutStyles.subtitle, children: "Paste a Linear token once. Rudder will read your teams and workflow states, then prepare the import settings automatically." }),
|
|
1071
|
+
bootstrap.data?.fixtureMode ? /* @__PURE__ */ jsx("div", { style: { marginTop: 12, ...layoutStyles.warning }, children: "Fixture mode is enabled in this environment. Linear reads use deterministic test data." }) : null
|
|
1072
|
+
] }, "linear-settings-intro"),
|
|
1073
|
+
/* @__PURE__ */ jsxs("section", { style: layoutStyles.card, children: [
|
|
1074
|
+
/* @__PURE__ */ jsx("div", { style: layoutStyles.sectionHeader, children: /* @__PURE__ */ jsxs("div", { children: [
|
|
1075
|
+
/* @__PURE__ */ jsx("h3", { style: { margin: 0 }, children: "Connect Linear" }),
|
|
1076
|
+
/* @__PURE__ */ jsxs("p", { style: layoutStyles.subtitle, children: [
|
|
1077
|
+
"Pick a Rudder organization, paste a token, and Rudder will prepare the import setup from Linear.",
|
|
1078
|
+
" ",
|
|
1079
|
+
/* @__PURE__ */ jsx("a", { href: LINEAR_TOKEN_SETTINGS_URL, target: "_blank", rel: "noreferrer", style: layoutStyles.monoLink, children: "Create a Linear token" }),
|
|
1080
|
+
"."
|
|
1081
|
+
] })
|
|
1082
|
+
] }) }),
|
|
1083
|
+
/* @__PURE__ */ jsxs("div", { style: { ...layoutStyles.connectionGrid, marginTop: 14 }, children: [
|
|
1084
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.field, children: [
|
|
1085
|
+
/* @__PURE__ */ jsx("label", { style: layoutStyles.label, htmlFor: "linear-token", children: "Linear token" }),
|
|
1086
|
+
/* @__PURE__ */ jsx(
|
|
1087
|
+
"input",
|
|
1088
|
+
{
|
|
1089
|
+
id: "linear-token",
|
|
1090
|
+
"data-testid": "linear-token-input",
|
|
1091
|
+
style: layoutStyles.input,
|
|
1092
|
+
type: "password",
|
|
1093
|
+
autoComplete: "off",
|
|
1094
|
+
value: tokenInput,
|
|
1095
|
+
onChange: (event) => setTokenInput(event.target.value),
|
|
1096
|
+
placeholder: draft.apiTokenSecretRef ? "Token saved. Paste a new token to replace it." : "Paste a Linear token"
|
|
1097
|
+
}
|
|
1098
|
+
)
|
|
1099
|
+
] }),
|
|
1100
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 6, justifySelf: "start" }, children: [
|
|
1101
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { ...layoutStyles.label, visibility: "hidden" }, children: "Action" }),
|
|
1102
|
+
/* @__PURE__ */ jsx(
|
|
1103
|
+
"button",
|
|
1104
|
+
{
|
|
1105
|
+
type: "button",
|
|
1106
|
+
style: layoutStyles.primaryButton,
|
|
1107
|
+
"data-testid": "linear-connect",
|
|
1108
|
+
onClick: () => void connectLinear(),
|
|
1109
|
+
disabled: connecting,
|
|
1110
|
+
children: connecting ? "Connecting\u2026" : draft.apiTokenSecretRef ? "Refresh from Linear" : "Connect Linear"
|
|
1111
|
+
}
|
|
1112
|
+
)
|
|
1113
|
+
] })
|
|
1114
|
+
] }),
|
|
1115
|
+
/* @__PURE__ */ jsx("div", { style: layoutStyles.statusLine, children: connectionStatusText() })
|
|
1116
|
+
] }, "linear-settings-connect"),
|
|
1117
|
+
draft.apiTokenSecretRef ? /* @__PURE__ */ jsxs("section", { style: layoutStyles.card, children: [
|
|
1118
|
+
/* @__PURE__ */ jsxs("div", { style: layoutStyles.sectionHeader, children: [
|
|
1119
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1120
|
+
/* @__PURE__ */ jsx("h3", { style: { margin: 0 }, children: "Teams to import" }),
|
|
1121
|
+
/* @__PURE__ */ jsx("p", { style: layoutStyles.subtitle, children: "Choose the Linear teams that should appear on the import page. Rudder stores the technical details for you." })
|
|
1122
|
+
] }),
|
|
1123
|
+
/* @__PURE__ */ jsx(
|
|
1124
|
+
"button",
|
|
1125
|
+
{
|
|
1126
|
+
type: "button",
|
|
1127
|
+
style: layoutStyles.subtleButton,
|
|
1128
|
+
onClick: selectAllTeams,
|
|
1129
|
+
disabled: !settingsCatalog || catalogLoading || catalogTeamCount === selectedTeamCount,
|
|
1130
|
+
children: "Select all teams"
|
|
1131
|
+
}
|
|
1132
|
+
)
|
|
1133
|
+
] }),
|
|
1134
|
+
catalogLoading ? /* @__PURE__ */ jsx("p", { style: layoutStyles.helpText, children: "Loading teams from Linear\u2026" }) : catalogError ? /* @__PURE__ */ jsxs("div", { style: { marginTop: 14, ...layoutStyles.warning }, children: [
|
|
1135
|
+
/* @__PURE__ */ jsx("strong", { children: "Linear could not be loaded." }),
|
|
1136
|
+
/* @__PURE__ */ jsx("div", { style: { marginTop: 6 }, children: catalogError })
|
|
1137
|
+
] }) : settingsCatalog ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1138
|
+
/* @__PURE__ */ jsx("div", { style: layoutStyles.teamGrid, children: settingsCatalog.teams.map((team, index) => {
|
|
1139
|
+
const checked = selectedTeamIdSet.has(team.id);
|
|
1140
|
+
return /* @__PURE__ */ jsxs(
|
|
1141
|
+
"label",
|
|
1142
|
+
{
|
|
1143
|
+
style: {
|
|
1144
|
+
...layoutStyles.teamChoice,
|
|
1145
|
+
borderColor: checked ? "var(--primary, #2563eb)" : "var(--border, rgba(15, 23, 42, 0.14))",
|
|
1146
|
+
background: checked ? "rgba(37, 99, 235, 0.08)" : "var(--background, transparent)"
|
|
1147
|
+
},
|
|
1148
|
+
children: [
|
|
1149
|
+
/* @__PURE__ */ jsx(
|
|
1150
|
+
"input",
|
|
1151
|
+
{
|
|
1152
|
+
"data-testid": `linear-team-choice-${team.id}`,
|
|
1153
|
+
style: layoutStyles.checkbox,
|
|
1154
|
+
type: "checkbox",
|
|
1155
|
+
checked,
|
|
1156
|
+
onChange: () => toggleTeam(team.id)
|
|
1157
|
+
}
|
|
1158
|
+
),
|
|
1159
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1160
|
+
/* @__PURE__ */ jsx("strong", { children: team.name }),
|
|
1161
|
+
/* @__PURE__ */ jsxs("span", { style: { display: "block", marginTop: 3, ...layoutStyles.helpText }, children: [
|
|
1162
|
+
team.key,
|
|
1163
|
+
" \xB7 ",
|
|
1164
|
+
team.states.length,
|
|
1165
|
+
" workflow state",
|
|
1166
|
+
team.states.length === 1 ? "" : "s"
|
|
1167
|
+
] })
|
|
1168
|
+
] })
|
|
1169
|
+
]
|
|
1170
|
+
},
|
|
1171
|
+
`${team.id}-${index}`
|
|
1172
|
+
);
|
|
1173
|
+
}) }),
|
|
1174
|
+
/* @__PURE__ */ jsxs("div", { style: { ...layoutStyles.row, justifyContent: "space-between", marginTop: 14 }, children: [
|
|
1175
|
+
/* @__PURE__ */ jsx("span", { style: layoutStyles.helpText, children: selectedTeamCount === 0 ? "Choose at least one team before saving." : `${selectedTeamCount} team${selectedTeamCount === 1 ? "" : "s"} selected for import.` }),
|
|
1176
|
+
/* @__PURE__ */ jsx(
|
|
1177
|
+
"button",
|
|
1178
|
+
{
|
|
1179
|
+
type: "button",
|
|
1180
|
+
style: layoutStyles.primaryButton,
|
|
1181
|
+
"data-testid": "linear-save-team-choices",
|
|
1182
|
+
onClick: () => void saveConfig(),
|
|
1183
|
+
disabled: saving || selectedTeamCount === 0,
|
|
1184
|
+
children: saving ? "Saving\u2026" : "Save choices"
|
|
1185
|
+
}
|
|
1186
|
+
)
|
|
1187
|
+
] })
|
|
1188
|
+
] }) : /* @__PURE__ */ jsx("p", { style: layoutStyles.helpText, children: "Refresh from Linear to load teams for this token." })
|
|
1189
|
+
] }, "linear-settings-teams") : null,
|
|
1190
|
+
settingsCatalog && draft.teamMappings.length > 0 ? /* @__PURE__ */ jsx("section", { style: layoutStyles.card, children: /* @__PURE__ */ jsxs("details", { children: [
|
|
1191
|
+
/* @__PURE__ */ jsx("summary", { style: { cursor: "pointer", fontWeight: 700 }, children: "Status rules (optional)" }),
|
|
1192
|
+
/* @__PURE__ */ jsx("p", { style: layoutStyles.subtitle, children: "Rudder applies smart defaults from Linear. Change these only if imported issues land in the wrong Rudder status." }),
|
|
1193
|
+
/* @__PURE__ */ jsx("div", { style: { display: "grid", gap: 18, marginTop: 14 }, children: draft.teamMappings.map((teamMapping, teamIndex) => {
|
|
1194
|
+
const catalogTeam = settingsCatalog.teams.find((team) => team.id === teamMapping.teamId);
|
|
1195
|
+
const states = catalogTeam?.states ?? teamMapping.stateMappings.map((state) => ({
|
|
1196
|
+
id: state.linearStateId,
|
|
1197
|
+
name: state.linearStateName ?? "Linear status",
|
|
1198
|
+
type: null
|
|
1199
|
+
}));
|
|
1200
|
+
const statusByStateId = new Map(teamMapping.stateMappings.map((state) => [state.linearStateId, state.rudderStatus]));
|
|
1201
|
+
return /* @__PURE__ */ jsxs("div", { style: { borderTop: "1px solid var(--border, rgba(15, 23, 42, 0.1))", paddingTop: 14 }, children: [
|
|
1202
|
+
/* @__PURE__ */ jsx("h4", { style: { margin: "0 0 10px" }, children: teamMapping.teamName ?? catalogTeam?.name ?? "Linear team" }),
|
|
1203
|
+
/* @__PURE__ */ jsx("div", { style: { display: "grid", gap: 8 }, children: states.map((state, stateIndex) => /* @__PURE__ */ jsxs("div", { style: layoutStyles.statusRuleGrid, children: [
|
|
1204
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1205
|
+
/* @__PURE__ */ jsx("strong", { children: state.name }),
|
|
1206
|
+
/* @__PURE__ */ jsx("div", { style: layoutStyles.helpText, children: state.type ? `${state.type} in Linear` : "Linear workflow state" })
|
|
1207
|
+
] }),
|
|
1208
|
+
/* @__PURE__ */ jsx(
|
|
1209
|
+
"select",
|
|
1210
|
+
{
|
|
1211
|
+
style: layoutStyles.select,
|
|
1212
|
+
value: statusByStateId.get(state.id) ?? inferRudderStatus(state),
|
|
1213
|
+
onChange: (event) => setStatusRule(teamMapping.teamId, state.id, event.target.value),
|
|
1214
|
+
children: RUDDER_STATUS_OPTIONS.map((status) => /* @__PURE__ */ jsx("option", { value: status, children: formatRudderStatus(status) }, `${teamMapping.teamId}-${state.id}-${status}`))
|
|
1215
|
+
}
|
|
1216
|
+
)
|
|
1217
|
+
] }, `${teamMapping.teamId}-${state.id}-${stateIndex}`)) })
|
|
1218
|
+
] }, `${teamMapping.teamId}-${teamIndex}`);
|
|
1219
|
+
}) }),
|
|
1220
|
+
/* @__PURE__ */ jsx("div", { style: { marginTop: 14 }, children: /* @__PURE__ */ jsx("button", { type: "button", style: layoutStyles.primaryButton, onClick: () => void saveConfig(), disabled: saving, children: saving ? "Saving\u2026" : "Save status rules" }) })
|
|
1221
|
+
] }) }, "linear-settings-status-rules") : null
|
|
1222
|
+
] });
|
|
1223
|
+
}
|
|
1224
|
+
export {
|
|
1225
|
+
LinearIssueTab,
|
|
1226
|
+
LinearPluginPage,
|
|
1227
|
+
LinearPluginSettingsPage
|
|
1228
|
+
};
|
|
1229
|
+
//# sourceMappingURL=index.js.map
|