@gaud_erp/paperclip-github-manager 0.4.0 → 1.0.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/ui/index.js CHANGED
@@ -1,627 +1,756 @@
1
- // src/ui/index.tsx
2
- import { useMemo, useState } from "react";
3
- import {
4
- useHostContext,
5
- useHostLocation,
6
- useHostNavigation,
7
- usePluginAction,
8
- usePluginData
9
- } from "@paperclipai/plugin-sdk/ui";
1
+ // src/ui/components/Sidebar.tsx
2
+ import { useHostNavigation, usePluginData } from "@paperclipai/plugin-sdk/ui";
10
3
 
11
- // src/constants.ts
4
+ // src/ui/components/shared.ts
12
5
  var PATHS = {
13
- repos: "/github",
14
6
  settings: "/github-settings",
15
- pullRequests: "/github-pull-requests",
16
- companySecrets: "/company/settings"
7
+ repos: "/github-repos",
8
+ prs: "/github-prs",
9
+ graphs: "/github-graphs"
17
10
  };
18
- var GITHUB_TOKEN_SECRET_KEY = "github_token";
19
-
20
- // src/ui/index.tsx
21
- import { jsx, jsxs } from "react/jsx-runtime";
22
- var panelStyle = {
23
- border: "1px solid rgba(255,255,255,0.12)",
24
- borderRadius: 8,
25
- padding: "1rem",
26
- display: "grid",
27
- gap: "0.75rem"
11
+ var layoutStack = {
12
+ display: "flex",
13
+ flexDirection: "column",
14
+ gap: "12px",
15
+ padding: "16px"
16
+ };
17
+ var cardStyle = {
18
+ border: "1px solid rgba(128,128,128,0.2)",
19
+ borderRadius: "8px",
20
+ padding: "12px",
21
+ background: "rgba(128,128,128,0.04)"
28
22
  };
29
23
  var buttonStyle = {
30
- padding: "0.4rem 0.75rem",
31
- borderRadius: 6,
32
- border: "1px solid rgba(255,255,255,0.2)",
33
- background: "rgba(255,255,255,0.06)",
34
- cursor: "pointer"
24
+ padding: "6px 12px",
25
+ borderRadius: "6px",
26
+ border: "1px solid rgba(128,128,128,0.3)",
27
+ background: "transparent",
28
+ cursor: "pointer",
29
+ fontSize: "13px"
35
30
  };
36
- var sidebarLinkClass = "flex w-full items-center gap-2.5 px-3 py-2 text-[13px] font-medium transition-colors no-underline";
37
- var sidebarLinkActiveClass = "bg-accent text-foreground";
38
- var sidebarLinkIdleClass = "text-foreground/80 hover:bg-accent/50 hover:text-foreground";
31
+ var primaryButtonStyle = {
32
+ ...buttonStyle,
33
+ background: "rgba(59,130,246,0.1)",
34
+ borderColor: "rgba(59,130,246,0.3)",
35
+ color: "#3b82f6"
36
+ };
37
+ var badgeStyle = (color) => ({
38
+ display: "inline-block",
39
+ padding: "2px 8px",
40
+ borderRadius: "12px",
41
+ fontSize: "11px",
42
+ fontWeight: 600,
43
+ background: `${color}20`,
44
+ color
45
+ });
46
+ function prStateBadge(state) {
47
+ switch (state) {
48
+ case "open":
49
+ return { label: "Open", color: "#22c55e" };
50
+ case "closed":
51
+ return { label: "Closed", color: "#ef4444" };
52
+ case "merged":
53
+ return { label: "Merged", color: "#a855f7" };
54
+ case "draft":
55
+ return { label: "Draft", color: "#6b7280" };
56
+ default:
57
+ return { label: state, color: "#6b7280" };
58
+ }
59
+ }
60
+ function timeAgo(dateStr) {
61
+ const diff = Date.now() - new Date(dateStr).getTime();
62
+ const mins = Math.floor(diff / 6e4);
63
+ if (mins < 60) return `${mins}m ago`;
64
+ const hours = Math.floor(mins / 60);
65
+ if (hours < 24) return `${hours}h ago`;
66
+ const days = Math.floor(hours / 24);
67
+ return `${days}d ago`;
68
+ }
69
+
70
+ // src/ui/components/Sidebar.tsx
71
+ import { jsx, jsxs } from "react/jsx-runtime";
39
72
  var NAV_ITEMS = [
40
- { path: PATHS.settings, label: "Configura\xE7\xF5es" },
41
- { path: PATHS.repos, label: "Reposit\xF3rios" },
42
- { path: PATHS.pullRequests, label: "Pull requests" }
73
+ { label: "Configura\xE7\xF5es", path: PATHS.settings },
74
+ { label: "Reposit\xF3rios", path: PATHS.repos },
75
+ { label: "Pull Requests", path: PATHS.prs },
76
+ { label: "Knowledge Graphs", path: PATHS.graphs }
43
77
  ];
44
- function useGithubCompany() {
45
- const { companyId } = useHostContext();
46
- const companyParams = useMemo(
47
- () => companyId ? { companyId } : void 0,
48
- [companyId]
78
+ function GitHubSidebarLink() {
79
+ const nav = useHostNavigation();
80
+ const href = nav.resolveHref(PATHS.repos);
81
+ const isActive = typeof window !== "undefined" && window.location.pathname === href;
82
+ return /* @__PURE__ */ jsxs(
83
+ "a",
84
+ {
85
+ ...nav.linkProps(PATHS.repos),
86
+ "aria-current": isActive ? "page" : void 0,
87
+ className: [
88
+ "flex items-center gap-2.5 px-3 py-2 text-[13px] font-medium",
89
+ isActive ? "bg-accent text-foreground" : "text-foreground/80 hover:bg-accent/50"
90
+ ].join(" "),
91
+ children: [
92
+ /* @__PURE__ */ jsx("span", { className: "relative shrink-0", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", className: "h-4 w-4", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z" }) }) }),
93
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: "GitHub" })
94
+ ]
95
+ }
49
96
  );
50
- return { companyId, companyParams };
51
97
  }
52
- function isPathActive(pathname, href) {
53
- return pathname === href || pathname.startsWith(`${href}/`);
98
+ function GitHubSidebarPanel() {
99
+ const syncStatus = usePluginData("sync-status");
100
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "4px", fontSize: "12px", padding: "8px" }, children: [
101
+ /* @__PURE__ */ jsx("strong", { children: "GitHub" }),
102
+ /* @__PURE__ */ jsxs("div", { children: [
103
+ "Repos: ",
104
+ syncStatus.data?.repoCount ?? 0
105
+ ] }),
106
+ /* @__PURE__ */ jsxs("div", { children: [
107
+ "PRs abertos: ",
108
+ syncStatus.data?.openPRCount ?? 0
109
+ ] }),
110
+ /* @__PURE__ */ jsxs("div", { children: [
111
+ "\xDAltimo sync: ",
112
+ syncStatus.data?.lastSync ? new Date(syncStatus.data.lastSync).toLocaleString() : "nunca"
113
+ ] })
114
+ ] });
54
115
  }
55
- function GitHubModuleNav() {
116
+ function GitHubRouteSidebar() {
56
117
  const nav = useHostNavigation();
57
- const { pathname } = useHostLocation();
58
- return /* @__PURE__ */ jsx("nav", { style: { display: "flex", flexDirection: "column", gap: "0.125rem" }, children: NAV_ITEMS.map((item) => {
118
+ return /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "2px", padding: "4px" }, children: NAV_ITEMS.map((item) => {
59
119
  const href = nav.resolveHref(item.path);
60
- const active = isPathActive(pathname, href);
120
+ const isActive = typeof window !== "undefined" && window.location.pathname === href;
61
121
  return /* @__PURE__ */ jsx(
62
122
  "a",
63
123
  {
64
124
  ...nav.linkProps(item.path),
65
- className: `${sidebarLinkClass} ${active ? sidebarLinkActiveClass : sidebarLinkIdleClass}`,
66
- children: /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: item.label })
125
+ "aria-current": isActive ? "page" : void 0,
126
+ style: {
127
+ display: "block",
128
+ padding: "6px 12px",
129
+ borderRadius: "6px",
130
+ fontSize: "13px",
131
+ fontWeight: isActive ? 600 : 400,
132
+ background: isActive ? "rgba(128,128,128,0.1)" : "transparent",
133
+ textDecoration: "none",
134
+ color: "inherit"
135
+ },
136
+ children: item.label
67
137
  },
68
138
  item.path
69
139
  );
70
140
  }) });
71
141
  }
72
- function GitHubSidebarModule(_props) {
73
- return /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: "0.25rem" }, children: [
74
- /* @__PURE__ */ jsx(
75
- "div",
76
- {
77
- style: {
78
- padding: "0.25rem 0.75rem",
79
- fontSize: "0.7rem",
80
- fontWeight: 600,
81
- letterSpacing: "0.04em",
82
- textTransform: "uppercase",
83
- opacity: 0.55
84
- },
85
- children: "GitHub"
86
- }
87
- ),
88
- /* @__PURE__ */ jsx(GitHubModuleNav, {})
89
- ] });
90
- }
91
- function GitHubRouteSidebar(_props) {
92
- const nav = useHostNavigation();
93
- return /* @__PURE__ */ jsxs("nav", { style: { display: "flex", flexDirection: "column", gap: "0.25rem", padding: "0.5rem 0" }, children: [
94
- /* @__PURE__ */ jsx(
95
- "a",
96
- {
97
- ...nav.linkProps("/dashboard"),
98
- className: `${sidebarLinkClass} ${sidebarLinkIdleClass}`,
99
- children: /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: "\u2190 Company dashboard" })
100
- }
101
- ),
102
- /* @__PURE__ */ jsx(
103
- "div",
104
- {
105
- style: {
106
- padding: "0.75rem 0.75rem 0.25rem",
107
- fontSize: "0.7rem",
108
- fontWeight: 600,
109
- letterSpacing: "0.04em",
110
- textTransform: "uppercase",
111
- opacity: 0.55
112
- },
113
- children: "GitHub"
114
- }
115
- ),
116
- /* @__PURE__ */ jsx(GitHubModuleNav, {})
117
- ] });
118
- }
119
- function RepoTable({ repos }) {
120
- if (repos.length === 0) {
121
- return /* @__PURE__ */ jsx("div", { children: "Nenhum reposit\xF3rio listado. Configure o token em Configura\xE7\xF5es." });
122
- }
123
- return /* @__PURE__ */ jsxs("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: "0.9rem" }, children: [
124
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { style: { textAlign: "left", opacity: 0.7 }, children: [
125
- /* @__PURE__ */ jsx("th", { style: { padding: "0.35rem 0" }, children: "Reposit\xF3rio" }),
126
- /* @__PURE__ */ jsx("th", { children: "Atualizado" }),
127
- /* @__PURE__ */ jsx("th", { children: "Visibilidade" })
128
- ] }) }),
129
- /* @__PURE__ */ jsx("tbody", { children: repos.map((repo) => /* @__PURE__ */ jsxs("tr", { children: [
130
- /* @__PURE__ */ jsx("td", { style: { padding: "0.35rem 0" }, children: /* @__PURE__ */ jsx("a", { href: repo.htmlUrl, target: "_blank", rel: "noreferrer", children: repo.fullName }) }),
131
- /* @__PURE__ */ jsx("td", { children: new Date(repo.updatedAt).toLocaleString() }),
132
- /* @__PURE__ */ jsx("td", { children: repo.private ? "private" : "public" })
133
- ] }, repo.id)) })
134
- ] });
135
- }
136
- function DashboardWidget(_props) {
137
- const { companyParams } = useGithubCompany();
138
- const { data, loading, error } = usePluginData("health", companyParams);
139
- const ping = usePluginAction("ping");
140
- const nav = useHostNavigation();
141
- if (loading) return /* @__PURE__ */ jsx("div", { children: "Carregando status do GitHub..." });
142
- if (error) return /* @__PURE__ */ jsxs("div", { children: [
143
- "Erro do plugin: ",
144
- error.message
145
- ] });
146
- return /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: "0.5rem" }, children: [
147
- /* @__PURE__ */ jsx("strong", { children: "GitHub Manager" }),
148
- /* @__PURE__ */ jsxs("div", { children: [
149
- "Status: ",
150
- data?.status ?? "unknown"
151
- ] }),
152
- data?.login ? /* @__PURE__ */ jsxs("div", { children: [
153
- "Conta: ",
154
- data.login
155
- ] }) : null,
156
- data?.message ? /* @__PURE__ */ jsx("div", { children: data.message }) : null,
157
- /* @__PURE__ */ jsxs("div", { children: [
158
- "Verificado: ",
159
- data?.checkedAt ?? "nunca"
160
- ] }),
161
- data?.status !== "ok" ? /* @__PURE__ */ jsx("a", { ...nav.linkProps(PATHS.settings), style: { fontSize: "0.85rem" }, children: "Configurar token \u2192" }) : null,
162
- /* @__PURE__ */ jsx("button", { style: buttonStyle, onClick: () => void ping(), children: "Ping Worker" })
163
- ] });
164
- }
165
- function GitHubSettingsPage(_props) {
166
- const nav = useHostNavigation();
167
- const { companyId, companyParams } = useGithubCompany();
168
- const { data: health, loading, error, refresh } = usePluginData("health", companyParams);
169
- const saveGithubToken = usePluginAction("saveGithubToken");
170
- const saveGithubSecretRef = usePluginAction("saveGithubSecretRef");
171
- const clearGithubAuth = usePluginAction("clearGithubAuth");
172
- const [pat, setPat] = useState("");
142
+
143
+ // src/ui/components/SettingsPage.tsx
144
+ import { useState } from "react";
145
+ import { useHostContext, usePluginAction } from "@paperclipai/plugin-sdk/ui";
146
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
147
+ function GitHubSettingsPage() {
148
+ const context = useHostContext();
149
+ const companyId = context.companyId;
150
+ const [token, setToken] = useState("");
173
151
  const [secretRef, setSecretRef] = useState("");
174
- const [busy, setBusy] = useState(null);
175
- const [formMessage, setFormMessage] = useState(null);
176
- async function runSave(label, fn) {
177
- if (!companyId) {
178
- setFormMessage("Selecione uma company no header do Paperclip.");
179
- return;
152
+ const [repoInput, setRepoInput] = useState("");
153
+ const [status, setStatus] = useState(null);
154
+ const [loading, setLoading] = useState(false);
155
+ const saveToken = usePluginAction("save-token");
156
+ const saveSecretRefAction = usePluginAction("save-secret-ref");
157
+ const testConnection = usePluginAction("test-connection");
158
+ const addRepo = usePluginAction("add-repo");
159
+ const syncAll = usePluginAction("sync-all");
160
+ if (!companyId) return /* @__PURE__ */ jsx2("div", { style: layoutStack, children: "Selecione uma empresa." });
161
+ const handleSaveToken = async () => {
162
+ if (!token.trim()) return;
163
+ setLoading(true);
164
+ try {
165
+ await saveToken({ companyId, token: token.trim() });
166
+ setStatus("Token salvo com sucesso");
167
+ setToken("");
168
+ } catch (err) {
169
+ setStatus(`Erro: ${err}`);
180
170
  }
181
- setBusy(label);
182
- setFormMessage(null);
171
+ setLoading(false);
172
+ };
173
+ const handleSaveSecretRef = async () => {
174
+ if (!secretRef.trim()) return;
175
+ setLoading(true);
183
176
  try {
184
- await fn();
185
- setPat("");
186
- setFormMessage(`${label} \u2014 OK. Testando conex\xE3o\u2026`);
187
- refresh();
177
+ await saveSecretRefAction({ companyId, secretRef: secretRef.trim() });
178
+ setStatus("Secret ref salvo com sucesso");
179
+ setSecretRef("");
188
180
  } catch (err) {
189
- setFormMessage(err instanceof Error ? err.message : String(err));
190
- } finally {
191
- setBusy(null);
181
+ setStatus(`Erro: ${err}`);
192
182
  }
193
- }
194
- return /* @__PURE__ */ jsxs("div", { style: { padding: "1.5rem", display: "grid", gap: "1rem", maxWidth: 720 }, children: [
195
- /* @__PURE__ */ jsxs("header", { children: [
196
- /* @__PURE__ */ jsx("h1", { style: { margin: 0 }, children: "GitHub \u2014 Configura\xE7\xF5es" }),
197
- /* @__PURE__ */ jsxs("p", { style: { margin: "0.5rem 0 0", opacity: 0.8 }, children: [
198
- "Conecte um Personal Access Token (PAT) da GitHub. O token fica no estado do plugin por company (o Paperclip ainda n\xE3o resolve ",
199
- /* @__PURE__ */ jsx("code", { children: "secrets.read-ref" }),
200
- " em workers \u2014 PAP-2394)."
201
- ] })
202
- ] }),
203
- !companyId ? /* @__PURE__ */ jsx("p", { style: { margin: 0, opacity: 0.8 }, children: "Selecione uma company no header do Paperclip." }) : null,
204
- /* @__PURE__ */ jsxs("section", { style: panelStyle, children: [
205
- /* @__PURE__ */ jsx("strong", { children: "1. Personal Access Token (recomendado)" }),
206
- /* @__PURE__ */ jsxs("p", { style: { margin: 0, opacity: 0.85 }, children: [
207
- "Gere um PAT em GitHub \u2192 Settings \u2192 Developer settings. Escopos: ",
208
- /* @__PURE__ */ jsx("code", { children: "repo" }),
209
- ",",
210
- " ",
211
- /* @__PURE__ */ jsx("code", { children: "read:user" }),
212
- "; para webhooks tamb\xE9m ",
213
- /* @__PURE__ */ jsx("code", { children: "admin:repo_hook" }),
214
- "."
215
- ] }),
216
- /* @__PURE__ */ jsxs("label", { style: { display: "grid", gap: "0.35rem" }, children: [
217
- "PAT",
218
- /* @__PURE__ */ jsx(
183
+ setLoading(false);
184
+ };
185
+ const handleTestConnection = async () => {
186
+ setLoading(true);
187
+ try {
188
+ const result = await testConnection({ companyId });
189
+ if (result.ok) {
190
+ setStatus(`Conectado como ${result.login}`);
191
+ } else {
192
+ setStatus(`Falha: ${result.error}`);
193
+ }
194
+ } catch (err) {
195
+ setStatus(`Erro: ${err}`);
196
+ }
197
+ setLoading(false);
198
+ };
199
+ const handleAddRepo = async () => {
200
+ if (!repoInput.trim()) return;
201
+ setLoading(true);
202
+ try {
203
+ await addRepo({ companyId, fullName: repoInput.trim() });
204
+ setStatus(`Reposit\xF3rio ${repoInput.trim()} adicionado`);
205
+ setRepoInput("");
206
+ } catch (err) {
207
+ setStatus(`Erro: ${err}`);
208
+ }
209
+ setLoading(false);
210
+ };
211
+ const handleFullSync = async () => {
212
+ setLoading(true);
213
+ setStatus("Sincronizando...");
214
+ try {
215
+ await syncAll({ companyId });
216
+ setStatus("Sync completo finalizado");
217
+ } catch (err) {
218
+ setStatus(`Erro no sync: ${err}`);
219
+ }
220
+ setLoading(false);
221
+ };
222
+ return /* @__PURE__ */ jsxs2("div", { style: layoutStack, children: [
223
+ /* @__PURE__ */ jsx2("h2", { style: { margin: 0, fontSize: "18px" }, children: "Configura\xE7\xF5es GitHub" }),
224
+ status && /* @__PURE__ */ jsx2("div", { style: { ...cardStyle, fontSize: "13px", color: status.startsWith("Erro") ? "#ef4444" : "#22c55e" }, children: status }),
225
+ /* @__PURE__ */ jsxs2("div", { style: cardStyle, children: [
226
+ /* @__PURE__ */ jsx2("h3", { style: { margin: "0 0 8px", fontSize: "14px" }, children: "Autentica\xE7\xE3o" }),
227
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: "8px", marginBottom: "8px" }, children: [
228
+ /* @__PURE__ */ jsx2(
219
229
  "input",
220
230
  {
221
231
  type: "password",
222
- autoComplete: "off",
223
- value: pat,
224
- onChange: (e) => setPat(e.target.value),
225
- placeholder: "ghp_\u2026",
226
- style: { maxWidth: 480, padding: "0.4rem 0.5rem" }
232
+ placeholder: "GitHub Personal Access Token",
233
+ value: token,
234
+ onChange: (e) => setToken(e.target.value),
235
+ style: { flex: 1, padding: "6px 10px", borderRadius: "6px", border: "1px solid rgba(128,128,128,0.3)", background: "transparent", fontSize: "13px" }
227
236
  }
228
- )
229
- ] }),
230
- /* @__PURE__ */ jsx(
231
- "button",
232
- {
233
- style: buttonStyle,
234
- type: "button",
235
- disabled: Boolean(busy) || !companyId || !pat.trim(),
236
- onClick: () => void runSave(
237
- "Salvar token",
238
- () => saveGithubToken({ companyId, token: pat })
239
- ),
240
- children: "Salvar token"
241
- }
242
- )
243
- ] }),
244
- /* @__PURE__ */ jsxs("section", { style: panelStyle, children: [
245
- /* @__PURE__ */ jsx("strong", { children: "2. Secret ID (futuro)" }),
246
- /* @__PURE__ */ jsxs("p", { style: { margin: 0, opacity: 0.85 }, children: [
247
- "Se voc\xEA j\xE1 criou um secret em ",
248
- /* @__PURE__ */ jsx("strong", { children: "Company \u2192 Settings" }),
249
- " (chave",
250
- " ",
251
- /* @__PURE__ */ jsx("code", { children: GITHUB_TOKEN_SECRET_KEY }),
252
- "), copie o ",
253
- /* @__PURE__ */ jsx("strong", { children: "ID (UUID)" }),
254
- " do secret \u2014 n\xE3o basta a chave. Quando o Paperclip reabilitar secret refs, este campo passar\xE1 a funcionar sem colar o PAT aqui."
237
+ ),
238
+ /* @__PURE__ */ jsx2("button", { type: "button", style: buttonStyle, onClick: handleSaveToken, disabled: loading, children: "Salvar PAT" })
255
239
  ] }),
256
- /* @__PURE__ */ jsx(
257
- "a",
258
- {
259
- ...nav.linkProps(PATHS.companySecrets),
260
- className: sidebarLinkClass,
261
- style: { display: "inline-flex", width: "auto", marginTop: "0.25rem" },
262
- children: "Abrir Company Settings"
263
- }
264
- ),
265
- /* @__PURE__ */ jsxs("label", { style: { display: "grid", gap: "0.35rem", marginTop: "0.75rem" }, children: [
266
- "Secret ID (UUID)",
267
- /* @__PURE__ */ jsx(
240
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: "8px", marginBottom: "8px" }, children: [
241
+ /* @__PURE__ */ jsx2(
268
242
  "input",
269
243
  {
244
+ placeholder: "UUID do secret (alternativa)",
270
245
  value: secretRef,
271
246
  onChange: (e) => setSecretRef(e.target.value),
272
- placeholder: "00000000-0000-0000-0000-000000000000",
273
- style: { maxWidth: 480, padding: "0.4rem 0.5rem", fontFamily: "monospace" }
247
+ style: { flex: 1, padding: "6px 10px", borderRadius: "6px", border: "1px solid rgba(128,128,128,0.3)", background: "transparent", fontSize: "13px" }
274
248
  }
275
- )
249
+ ),
250
+ /* @__PURE__ */ jsx2("button", { type: "button", style: buttonStyle, onClick: handleSaveSecretRef, disabled: loading, children: "Salvar Ref" })
276
251
  ] }),
277
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: "0.5rem" }, children: [
278
- /* @__PURE__ */ jsx(
279
- "button",
252
+ /* @__PURE__ */ jsx2("button", { type: "button", style: primaryButtonStyle, onClick: handleTestConnection, disabled: loading, children: "Testar Conex\xE3o" })
253
+ ] }),
254
+ /* @__PURE__ */ jsxs2("div", { style: cardStyle, children: [
255
+ /* @__PURE__ */ jsx2("h3", { style: { margin: "0 0 8px", fontSize: "14px" }, children: "Adicionar Reposit\xF3rio" }),
256
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: "8px" }, children: [
257
+ /* @__PURE__ */ jsx2(
258
+ "input",
280
259
  {
281
- style: buttonStyle,
282
- type: "button",
283
- disabled: Boolean(busy) || !companyId || !secretRef.trim(),
284
- onClick: () => void runSave(
285
- "Salvar Secret ID",
286
- () => saveGithubSecretRef({ companyId, secretRef })
287
- ),
288
- children: "Salvar Secret ID"
260
+ placeholder: "owner/repo (ex: gauderp/gaud-erp-api)",
261
+ value: repoInput,
262
+ onChange: (e) => setRepoInput(e.target.value),
263
+ style: { flex: 1, padding: "6px 10px", borderRadius: "6px", border: "1px solid rgba(128,128,128,0.3)", background: "transparent", fontSize: "13px" }
289
264
  }
290
265
  ),
291
- /* @__PURE__ */ jsx(
266
+ /* @__PURE__ */ jsx2("button", { type: "button", style: primaryButtonStyle, onClick: handleAddRepo, disabled: loading, children: "Adicionar" })
267
+ ] })
268
+ ] }),
269
+ /* @__PURE__ */ jsxs2("div", { style: cardStyle, children: [
270
+ /* @__PURE__ */ jsx2("h3", { style: { margin: "0 0 8px", fontSize: "14px" }, children: "Sincroniza\xE7\xE3o" }),
271
+ /* @__PURE__ */ jsx2("p", { style: { margin: "0 0 8px", fontSize: "12px", opacity: 0.7 }, children: "Sync autom\xE1tico a cada 5 minutos. Use o bot\xE3o abaixo para for\xE7ar um sync completo." }),
272
+ /* @__PURE__ */ jsx2("button", { type: "button", style: primaryButtonStyle, onClick: handleFullSync, disabled: loading, children: loading ? "Sincronizando..." : "Sync Completo" })
273
+ ] })
274
+ ] });
275
+ }
276
+
277
+ // src/ui/components/ReposPage.tsx
278
+ import { useState as useState2 } from "react";
279
+ import { useHostContext as useHostContext2, usePluginData as usePluginData2, usePluginAction as usePluginAction2 } from "@paperclipai/plugin-sdk/ui";
280
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
281
+ function GitHubReposPage() {
282
+ const context = useHostContext2();
283
+ const companyId = context.companyId;
284
+ const [filter, setFilter] = useState2("");
285
+ const reposData = usePluginData2("repos", { companyId });
286
+ const syncAction = usePluginAction2("sync-incremental");
287
+ const generateGraph = usePluginAction2("generate-graph");
288
+ if (!companyId) return /* @__PURE__ */ jsx3("div", { style: layoutStack, children: "Selecione uma empresa." });
289
+ const repos = (reposData.data?.repos ?? []).filter(
290
+ (r) => !filter || r.fullName.toLowerCase().includes(filter.toLowerCase())
291
+ );
292
+ return /* @__PURE__ */ jsxs3("div", { style: layoutStack, children: [
293
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
294
+ /* @__PURE__ */ jsxs3("h2", { style: { margin: 0, fontSize: "18px" }, children: [
295
+ "Reposit\xF3rios (",
296
+ repos.length,
297
+ ")"
298
+ ] }),
299
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
300
+ /* @__PURE__ */ jsxs3("span", { style: { fontSize: "11px", opacity: 0.6 }, children: [
301
+ "\xDAltimo sync: ",
302
+ reposData.data?.lastSync ? timeAgo(reposData.data.lastSync) : "nunca"
303
+ ] }),
304
+ /* @__PURE__ */ jsx3(
292
305
  "button",
293
306
  {
294
- style: buttonStyle,
295
307
  type: "button",
296
- disabled: Boolean(busy) || !companyId,
297
- onClick: () => void runSave("Limpar credenciais", () => clearGithubAuth({ companyId })),
298
- children: "Limpar"
308
+ style: buttonStyle,
309
+ onClick: () => syncAction({ companyId }).catch(console.error),
310
+ children: "Sync"
299
311
  }
300
312
  )
301
313
  ] })
302
314
  ] }),
303
- /* @__PURE__ */ jsxs("section", { style: panelStyle, children: [
304
- /* @__PURE__ */ jsx("strong", { children: "3. Testar conex\xE3o" }),
305
- health?.auth ? /* @__PURE__ */ jsxs("div", { style: { fontSize: "0.85rem", opacity: 0.75 }, children: [
306
- "Credencial: ",
307
- health.auth.configured ? health.auth.mode : "nenhuma"
308
- ] }) : null,
309
- loading ? /* @__PURE__ */ jsx("div", { children: "Verificando..." }) : null,
310
- error ? /* @__PURE__ */ jsxs("div", { children: [
311
- "Erro: ",
312
- error.message
313
- ] }) : null,
314
- health ? /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: "0.35rem" }, children: [
315
- /* @__PURE__ */ jsxs("div", { children: [
316
- "Status: ",
317
- /* @__PURE__ */ jsx("strong", { children: health.status }),
318
- health.login ? ` \u2014 @${health.login}` : ""
315
+ /* @__PURE__ */ jsx3(
316
+ "input",
317
+ {
318
+ placeholder: "Filtrar reposit\xF3rios...",
319
+ value: filter,
320
+ onChange: (e) => setFilter(e.target.value),
321
+ style: { padding: "6px 10px", borderRadius: "6px", border: "1px solid rgba(128,128,128,0.3)", background: "transparent", fontSize: "13px" }
322
+ }
323
+ ),
324
+ repos.length === 0 && /* @__PURE__ */ jsx3("div", { style: { ...cardStyle, textAlign: "center", opacity: 0.6 }, children: "Nenhum reposit\xF3rio rastreado. Adicione em Configura\xE7\xF5es." }),
325
+ repos.map((repo) => /* @__PURE__ */ jsxs3("div", { style: cardStyle, children: [
326
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start" }, children: [
327
+ /* @__PURE__ */ jsxs3("div", { children: [
328
+ /* @__PURE__ */ jsx3("a", { href: repo.htmlUrl, target: "_blank", rel: "noopener", style: { fontWeight: 600, fontSize: "14px", color: "#3b82f6", textDecoration: "none" }, children: repo.fullName }),
329
+ repo.private && /* @__PURE__ */ jsx3("span", { style: { marginLeft: "8px", fontSize: "11px", opacity: 0.5 }, children: "privado" }),
330
+ repo.description && /* @__PURE__ */ jsx3("p", { style: { margin: "4px 0 0", fontSize: "12px", opacity: 0.7 }, children: repo.description })
331
+ ] }),
332
+ /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: "4px" }, children: /* @__PURE__ */ jsx3(
333
+ "button",
334
+ {
335
+ type: "button",
336
+ style: buttonStyle,
337
+ onClick: () => generateGraph({ companyId, repoFullName: repo.fullName, level: "code" }).catch(console.error),
338
+ title: "Gerar Knowledge Graph",
339
+ children: "Graphify"
340
+ }
341
+ ) })
342
+ ] }),
343
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "16px", marginTop: "8px", fontSize: "11px", opacity: 0.5 }, children: [
344
+ repo.language && /* @__PURE__ */ jsx3("span", { children: repo.language }),
345
+ /* @__PURE__ */ jsxs3("span", { children: [
346
+ "branch: ",
347
+ repo.defaultBranch
319
348
  ] }),
320
- health.message ? /* @__PURE__ */ jsx("div", { children: health.message }) : null,
321
- /* @__PURE__ */ jsxs("div", { style: { fontSize: "0.85rem", opacity: 0.7 }, children: [
322
- "Verificado: ",
323
- new Date(health.checkedAt).toLocaleString()
349
+ /* @__PURE__ */ jsxs3("span", { children: [
350
+ "sync: ",
351
+ timeAgo(repo.syncedAt)
324
352
  ] })
325
- ] }) : null,
326
- /* @__PURE__ */ jsx(
353
+ ] })
354
+ ] }, repo.id))
355
+ ] });
356
+ }
357
+
358
+ // src/ui/components/PullRequestsPage.tsx
359
+ import { useState as useState3 } from "react";
360
+ import { useHostContext as useHostContext3, usePluginData as usePluginData3, usePluginAction as usePluginAction3 } from "@paperclipai/plugin-sdk/ui";
361
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
362
+ function GitHubPullRequestsPage() {
363
+ const context = useHostContext3();
364
+ const companyId = context.companyId;
365
+ const [stateFilter, setStateFilter] = useState3("open");
366
+ const [search, setSearch] = useState3("");
367
+ const prsData = usePluginData3("pull-requests", {
368
+ companyId,
369
+ filters: stateFilter ? { state: stateFilter } : void 0
370
+ });
371
+ const syncAction = usePluginAction3("sync-incremental");
372
+ if (!companyId) return /* @__PURE__ */ jsx4("div", { style: layoutStack, children: "Selecione uma empresa." });
373
+ const prs = (prsData.data?.pullRequests ?? []).filter(
374
+ (pr) => !search || pr.title.toLowerCase().includes(search.toLowerCase()) || pr.repoFullName.toLowerCase().includes(search.toLowerCase())
375
+ );
376
+ return /* @__PURE__ */ jsxs4("div", { style: layoutStack, children: [
377
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
378
+ /* @__PURE__ */ jsxs4("h2", { style: { margin: 0, fontSize: "18px" }, children: [
379
+ "Pull Requests (",
380
+ prs.length,
381
+ ")"
382
+ ] }),
383
+ /* @__PURE__ */ jsx4("button", { type: "button", style: buttonStyle, onClick: () => syncAction({ companyId }).catch(console.error), children: "Sync" })
384
+ ] }),
385
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap" }, children: [
386
+ ["open", "closed", "merged", ""].map((state) => /* @__PURE__ */ jsx4(
327
387
  "button",
328
388
  {
329
- style: buttonStyle,
330
389
  type: "button",
331
- disabled: Boolean(busy) || !companyId,
332
- onClick: () => refresh(),
333
- children: "Testar conex\xE3o"
390
+ style: {
391
+ ...buttonStyle,
392
+ background: stateFilter === state ? "rgba(128,128,128,0.15)" : "transparent",
393
+ fontWeight: stateFilter === state ? 600 : 400
394
+ },
395
+ onClick: () => setStateFilter(state),
396
+ children: state || "Todos"
397
+ },
398
+ state
399
+ )),
400
+ /* @__PURE__ */ jsx4(
401
+ "input",
402
+ {
403
+ placeholder: "Buscar por t\xEDtulo ou repo...",
404
+ value: search,
405
+ onChange: (e) => setSearch(e.target.value),
406
+ style: { flex: 1, minWidth: "200px", padding: "6px 10px", borderRadius: "6px", border: "1px solid rgba(128,128,128,0.3)", background: "transparent", fontSize: "13px" }
334
407
  }
335
- ),
336
- health?.status === "ok" ? /* @__PURE__ */ jsxs("p", { style: { margin: 0, color: "inherit", opacity: 0.85 }, children: [
337
- "Token OK. Use ",
338
- /* @__PURE__ */ jsx("a", { ...nav.linkProps(PATHS.repos), children: "Reposit\xF3rios" }),
339
- " e",
340
- " ",
341
- /* @__PURE__ */ jsx("a", { ...nav.linkProps(PATHS.pullRequests), children: "Pull requests" }),
342
- "."
343
- ] }) : null
408
+ )
344
409
  ] }),
345
- formMessage ? /* @__PURE__ */ jsx("div", { children: formMessage }) : null,
346
- busy ? /* @__PURE__ */ jsxs("div", { children: [
347
- "Executando: ",
348
- busy,
349
- "\u2026"
350
- ] }) : null
410
+ prs.length === 0 && /* @__PURE__ */ jsx4("div", { style: { ...cardStyle, textAlign: "center", opacity: 0.6 }, children: "Nenhum PR encontrado com os filtros atuais." }),
411
+ prs.map((pr) => {
412
+ const badge = prStateBadge(pr.draft ? "draft" : pr.state);
413
+ return /* @__PURE__ */ jsx4("div", { style: cardStyle, children: /* @__PURE__ */ jsx4("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start" }, children: /* @__PURE__ */ jsxs4("div", { style: { flex: 1 }, children: [
414
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
415
+ /* @__PURE__ */ jsx4("span", { style: badgeStyle(badge.color), children: badge.label }),
416
+ /* @__PURE__ */ jsxs4("a", { href: pr.htmlUrl, target: "_blank", rel: "noopener", style: { fontWeight: 600, fontSize: "14px", color: "#3b82f6", textDecoration: "none" }, children: [
417
+ "#",
418
+ pr.number,
419
+ " ",
420
+ pr.title
421
+ ] })
422
+ ] }),
423
+ /* @__PURE__ */ jsxs4("div", { style: { marginTop: "4px", fontSize: "12px", opacity: 0.6 }, children: [
424
+ pr.repoFullName,
425
+ " \xB7 ",
426
+ pr.author,
427
+ " \xB7 ",
428
+ pr.headBranch,
429
+ " \u2192 ",
430
+ pr.baseBranch,
431
+ " \xB7 ",
432
+ timeAgo(pr.updatedAt)
433
+ ] })
434
+ ] }) }) }, pr.id);
435
+ })
351
436
  ] });
352
437
  }
353
- function GitHubReposPage(_props) {
354
- const nav = useHostNavigation();
355
- const { companyId, companyParams } = useGithubCompany();
356
- const { data: health, loading: healthLoading, error: healthError } = usePluginData("health", companyParams);
357
- const {
358
- data: reposData,
359
- loading: reposLoading,
360
- error: reposError,
361
- refresh: refreshRepos
362
- } = usePluginData("repos", companyParams);
363
- const {
364
- data: webhookData,
365
- loading: webhookLoading,
366
- refresh: refreshWebhook
367
- } = usePluginData("webhookConfig", companyParams);
368
- const setTrackedRepos = usePluginAction("setTrackedRepos");
369
- const configureWebhook = usePluginAction("configureWebhook");
370
- const [selectedRepo, setSelectedRepo] = useState("");
371
- const [busy, setBusy] = useState(null);
372
- const [actionMessage, setActionMessage] = useState(null);
373
- const trackedSelection = selectedRepo || reposData?.repos[0]?.fullName || "";
374
- const needsToken = health?.status === "degraded" || health?.status === "error";
375
- async function runAction(label, fn) {
376
- if (!companyId) {
377
- setActionMessage("Selecione uma company no host antes de continuar.");
378
- return;
438
+
439
+ // src/ui/components/GraphsPage.tsx
440
+ import { useState as useState4 } from "react";
441
+ import { useHostContext as useHostContext4, usePluginAction as usePluginAction4 } from "@paperclipai/plugin-sdk/ui";
442
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
443
+ function GitHubGraphsPage() {
444
+ const context = useHostContext4();
445
+ const companyId = context.companyId;
446
+ const [graphData, setGraphData] = useState4(null);
447
+ const [loading, setLoading] = useState4(false);
448
+ const [repoInput, setRepoInput] = useState4("");
449
+ const generateGraph = usePluginAction4("generate-graph");
450
+ if (!companyId) return /* @__PURE__ */ jsx5("div", { style: layoutStack, children: "Selecione uma empresa." });
451
+ const handleGenerateHighLevel = async () => {
452
+ setLoading(true);
453
+ try {
454
+ const result = await generateGraph({ companyId, level: "high" });
455
+ setGraphData(result);
456
+ } catch (err) {
457
+ console.error(err);
379
458
  }
380
- setBusy(label);
381
- setActionMessage(null);
459
+ setLoading(false);
460
+ };
461
+ const handleGenerateCode = async () => {
462
+ if (!repoInput.trim()) return;
463
+ setLoading(true);
382
464
  try {
383
- await fn();
384
- refreshRepos();
385
- refreshWebhook();
386
- setActionMessage(`${label} conclu\xEDdo.`);
465
+ const result = await generateGraph({ companyId, repoFullName: repoInput.trim(), level: "code" });
466
+ setGraphData(result);
387
467
  } catch (err) {
388
- setActionMessage(err instanceof Error ? err.message : String(err));
389
- } finally {
390
- setBusy(null);
468
+ console.error(err);
391
469
  }
392
- }
393
- return /* @__PURE__ */ jsxs("div", { style: { padding: "1.5rem", display: "grid", gap: "1rem", maxWidth: 960 }, children: [
394
- /* @__PURE__ */ jsxs("header", { children: [
395
- /* @__PURE__ */ jsx("h1", { style: { margin: 0 }, children: "GitHub \u2014 Reposit\xF3rios" }),
396
- /* @__PURE__ */ jsx("p", { style: { margin: "0.5rem 0 0", opacity: 0.8 }, children: "Liste reposit\xF3rios e escolha quais rastrear para sync e webhooks." })
397
- ] }),
398
- needsToken ? /* @__PURE__ */ jsxs("section", { style: panelStyle, children: [
399
- /* @__PURE__ */ jsx("strong", { children: "Token n\xE3o configurado" }),
400
- /* @__PURE__ */ jsxs("p", { style: { margin: 0 }, children: [
401
- /* @__PURE__ */ jsx("a", { ...nav.linkProps(PATHS.settings), children: "Abrir Configura\xE7\xF5es" }),
402
- " e salve um PAT da GitHub."
403
- ] })
404
- ] }) : null,
405
- /* @__PURE__ */ jsxs("section", { style: panelStyle, children: [
406
- /* @__PURE__ */ jsx("strong", { children: "Conex\xE3o" }),
407
- healthLoading ? /* @__PURE__ */ jsx("div", { children: "Carregando..." }) : null,
408
- healthError ? /* @__PURE__ */ jsxs("div", { children: [
409
- "Erro: ",
410
- healthError.message
411
- ] }) : null,
412
- health ? /* @__PURE__ */ jsxs("div", { children: [
413
- "Status: ",
414
- health.status,
415
- health.login ? ` \u2014 ${health.login}` : "",
416
- health.message ? /* @__PURE__ */ jsx("div", { children: health.message }) : null
417
- ] }) : null
418
- ] }),
419
- /* @__PURE__ */ jsxs("section", { style: panelStyle, children: [
420
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", gap: "0.5rem" }, children: [
421
- /* @__PURE__ */ jsx("strong", { children: "Reposit\xF3rios" }),
422
- /* @__PURE__ */ jsx("button", { style: buttonStyle, disabled: Boolean(busy), onClick: () => refreshRepos(), children: "Atualizar lista" })
423
- ] }),
424
- reposLoading ? /* @__PURE__ */ jsx("div", { children: "Carregando reposit\xF3rios..." }) : null,
425
- reposError ? /* @__PURE__ */ jsxs("div", { children: [
426
- "Erro: ",
427
- reposError.message
428
- ] }) : null,
429
- reposData?.message ? /* @__PURE__ */ jsx("div", { children: reposData.message }) : null,
430
- reposData ? /* @__PURE__ */ jsx(RepoTable, { repos: reposData.repos }) : null,
431
- reposData?.repos.length ? /* @__PURE__ */ jsxs("label", { style: { display: "grid", gap: "0.35rem" }, children: [
432
- "Repo para webhook",
433
- /* @__PURE__ */ jsx(
434
- "select",
470
+ setLoading(false);
471
+ };
472
+ return /* @__PURE__ */ jsxs5("div", { style: layoutStack, children: [
473
+ /* @__PURE__ */ jsx5("h2", { style: { margin: 0, fontSize: "18px" }, children: "Knowledge Graphs" }),
474
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap" }, children: [
475
+ /* @__PURE__ */ jsx5("button", { type: "button", style: primaryButtonStyle, onClick: handleGenerateHighLevel, disabled: loading, children: loading ? "Gerando..." : "Grafo de Alto N\xEDvel" }),
476
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex", gap: "4px", flex: 1 }, children: [
477
+ /* @__PURE__ */ jsx5(
478
+ "input",
435
479
  {
436
- value: trackedSelection,
437
- onChange: (e) => setSelectedRepo(e.target.value),
438
- style: { maxWidth: 420 },
439
- children: reposData.repos.map((repo) => /* @__PURE__ */ jsx("option", { value: repo.fullName, children: repo.fullName }, repo.id))
480
+ placeholder: "owner/repo para drill-down...",
481
+ value: repoInput,
482
+ onChange: (e) => setRepoInput(e.target.value),
483
+ style: { flex: 1, padding: "6px 10px", borderRadius: "6px", border: "1px solid rgba(128,128,128,0.3)", background: "transparent", fontSize: "13px" }
440
484
  }
441
- )
442
- ] }) : null,
443
- /* @__PURE__ */ jsx(
444
- "button",
445
- {
446
- style: buttonStyle,
447
- disabled: Boolean(busy) || !trackedSelection || !companyId || needsToken,
448
- onClick: () => void runAction(
449
- "Salvar repos rastreados",
450
- () => setTrackedRepos({
451
- companyId,
452
- repos: reposData?.repos.slice(0, 5).map((r) => r.fullName) ?? []
453
- })
454
- ),
455
- children: "Rastrear top 5 repos"
456
- }
457
- )
485
+ ),
486
+ /* @__PURE__ */ jsx5("button", { type: "button", style: buttonStyle, onClick: handleGenerateCode, disabled: loading || !repoInput.trim(), children: "Grafo de C\xF3digo" })
487
+ ] })
458
488
  ] }),
459
- /* @__PURE__ */ jsxs("section", { style: panelStyle, children: [
460
- /* @__PURE__ */ jsx("strong", { children: "Webhooks" }),
461
- webhookLoading ? /* @__PURE__ */ jsx("div", { children: "Carregando configura\xE7\xE3o..." }) : null,
462
- /* @__PURE__ */ jsxs("div", { style: { fontSize: "0.85rem", opacity: 0.85 }, children: [
463
- "URL inbound: ",
464
- /* @__PURE__ */ jsx("code", { children: webhookData?.inboundUrl ?? "\u2014" })
489
+ graphData && /* @__PURE__ */ jsxs5("div", { style: cardStyle, children: [
490
+ /* @__PURE__ */ jsxs5("div", { style: { marginBottom: "8px", fontSize: "13px" }, children: [
491
+ /* @__PURE__ */ jsx5("strong", { children: graphData.level === "high" ? "Vis\xE3o Geral" : graphData.repoFullName }),
492
+ /* @__PURE__ */ jsxs5("span", { style: { marginLeft: "8px", fontSize: "11px", opacity: 0.5 }, children: [
493
+ graphData.nodes.length,
494
+ " n\xF3s \xB7 ",
495
+ graphData.edges.length,
496
+ " arestas \xB7 ",
497
+ new Date(graphData.generatedAt).toLocaleString()
498
+ ] })
465
499
  ] }),
466
- webhookData?.configured && webhookData.config ? /* @__PURE__ */ jsxs("div", { children: [
467
- "Configurado para ",
468
- /* @__PURE__ */ jsx("strong", { children: webhookData.config.repoFullName }),
469
- " (hook",
470
- " ",
471
- webhookData.config.hookId ?? "?",
472
- ") em",
473
- " ",
474
- new Date(webhookData.config.configuredAt).toLocaleString()
475
- ] }) : /* @__PURE__ */ jsx("div", { children: "Nenhum webhook registrado nesta company." }),
476
- /* @__PURE__ */ jsx(
477
- "button",
478
- {
479
- style: buttonStyle,
480
- disabled: Boolean(busy) || !companyId || !trackedSelection || needsToken,
481
- onClick: () => void runAction(
482
- "Registrar webhook",
483
- () => configureWebhook({
484
- companyId,
485
- repoFullName: trackedSelection,
486
- events: ["pull_request", "issues"]
487
- })
488
- ),
489
- children: "Registrar webhook no GitHub"
490
- }
491
- )
500
+ /* @__PURE__ */ jsxs5("div", { style: { maxHeight: "400px", overflow: "auto", fontSize: "12px" }, children: [
501
+ /* @__PURE__ */ jsxs5("div", { style: { marginBottom: "8px" }, children: [
502
+ /* @__PURE__ */ jsx5("strong", { children: "N\xF3s:" }),
503
+ graphData.nodes.map((node) => /* @__PURE__ */ jsxs5("div", { style: { padding: "2px 0", paddingLeft: "12px" }, children: [
504
+ /* @__PURE__ */ jsxs5("span", { style: { opacity: 0.5 }, children: [
505
+ "[",
506
+ node.type,
507
+ "]"
508
+ ] }),
509
+ " ",
510
+ node.label
511
+ ] }, node.id))
512
+ ] }),
513
+ /* @__PURE__ */ jsxs5("div", { children: [
514
+ /* @__PURE__ */ jsx5("strong", { children: "Arestas:" }),
515
+ graphData.edges.map((edge, i) => /* @__PURE__ */ jsxs5("div", { style: { padding: "2px 0", paddingLeft: "12px" }, children: [
516
+ edge.source,
517
+ " \u2192 ",
518
+ edge.target,
519
+ " ",
520
+ /* @__PURE__ */ jsxs5("span", { style: { opacity: 0.5 }, children: [
521
+ "(",
522
+ edge.label,
523
+ ")"
524
+ ] })
525
+ ] }, i))
526
+ ] })
527
+ ] })
492
528
  ] }),
493
- actionMessage ? /* @__PURE__ */ jsx("div", { children: actionMessage }) : null,
494
- busy ? /* @__PURE__ */ jsxs("div", { children: [
495
- "Executando: ",
496
- busy,
497
- "..."
498
- ] }) : null
529
+ !graphData && !loading && /* @__PURE__ */ jsx5("div", { style: { ...cardStyle, textAlign: "center", opacity: 0.5 }, children: "Clique em um dos bot\xF5es acima para gerar um knowledge graph." })
499
530
  ] });
500
531
  }
501
- function GitHubPullRequestsPage(_props) {
502
- const nav = useHostNavigation();
503
- const { companyId, companyParams } = useGithubCompany();
504
- const { data: health } = usePluginData("health", companyParams);
505
- const {
506
- data: syncOverview,
507
- loading: syncLoading,
508
- error: syncError,
509
- refresh: refreshSync
510
- } = usePluginData("syncOverview", companyParams);
511
- const syncAll = usePluginAction("syncAll");
512
- const syncPullRequests = usePluginAction("syncPullRequests");
513
- const syncIssues = usePluginAction("syncIssues");
514
- const [busy, setBusy] = useState(null);
515
- const [actionMessage, setActionMessage] = useState(null);
516
- const needsToken = health?.status === "degraded" || health?.status === "error";
517
- async function runAction(label, fn) {
518
- if (!companyId) {
519
- setActionMessage("Selecione uma company no host.");
520
- return;
521
- }
522
- setBusy(label);
523
- setActionMessage(null);
532
+
533
+ // src/ui/components/DetailTab.tsx
534
+ import { useState as useState6 } from "react";
535
+ import { usePluginData as usePluginData5, usePluginAction as usePluginAction6 } from "@paperclipai/plugin-sdk/ui";
536
+
537
+ // src/ui/components/ReviewDropdown.tsx
538
+ import { useState as useState5 } from "react";
539
+ import { usePluginData as usePluginData4, usePluginAction as usePluginAction5 } from "@paperclipai/plugin-sdk/ui";
540
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
541
+ function ReviewDropdown({ companyId, prId, repoFullName, prNumber }) {
542
+ const [open, setOpen] = useState5(false);
543
+ const [reviewing, setReviewing] = useState5(false);
544
+ const agentsData = usePluginData4("available-agents", { companyId });
545
+ const requestReview = usePluginAction5("request-review");
546
+ const agents = agentsData.data?.agents ?? [];
547
+ const handleReview = async (agentId) => {
548
+ setReviewing(true);
549
+ setOpen(false);
524
550
  try {
525
- await fn();
526
- refreshSync();
527
- setActionMessage(`${label} conclu\xEDdo.`);
551
+ await requestReview({ companyId, prId, repoFullName, prNumber, agentId });
528
552
  } catch (err) {
529
- setActionMessage(err instanceof Error ? err.message : String(err));
530
- } finally {
531
- setBusy(null);
553
+ console.error("Review request failed:", err);
532
554
  }
555
+ setReviewing(false);
556
+ };
557
+ if (reviewing) {
558
+ return /* @__PURE__ */ jsx6("span", { style: { fontSize: "12px", opacity: 0.6 }, children: "Revisando..." });
559
+ }
560
+ return /* @__PURE__ */ jsxs6("div", { style: { position: "relative", display: "inline-block" }, children: [
561
+ /* @__PURE__ */ jsx6("button", { type: "button", style: primaryButtonStyle, onClick: () => setOpen(!open), children: "Revisar \u25BE" }),
562
+ open && /* @__PURE__ */ jsxs6("div", { style: {
563
+ position: "absolute",
564
+ top: "100%",
565
+ right: 0,
566
+ marginTop: "4px",
567
+ background: "var(--background, #1a1a1a)",
568
+ border: "1px solid rgba(128,128,128,0.3)",
569
+ borderRadius: "8px",
570
+ padding: "4px",
571
+ minWidth: "200px",
572
+ zIndex: 10,
573
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)"
574
+ }, children: [
575
+ agents.length === 0 && /* @__PURE__ */ jsx6("div", { style: { padding: "8px", fontSize: "12px", opacity: 0.5 }, children: "Nenhum agente dispon\xEDvel" }),
576
+ agents.map((agent) => /* @__PURE__ */ jsxs6(
577
+ "button",
578
+ {
579
+ type: "button",
580
+ style: { ...buttonStyle, width: "100%", textAlign: "left", border: "none", borderRadius: "4px" },
581
+ onClick: () => handleReview(agent.id),
582
+ children: [
583
+ /* @__PURE__ */ jsx6("div", { style: { fontWeight: 500 }, children: agent.displayName }),
584
+ /* @__PURE__ */ jsx6("div", { style: { fontSize: "11px", opacity: 0.5 }, children: agent.role })
585
+ ]
586
+ },
587
+ agent.id
588
+ ))
589
+ ] })
590
+ ] });
591
+ }
592
+
593
+ // src/ui/components/DetailTab.tsx
594
+ import { Fragment, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
595
+ function GitHubDetailTab({ context }) {
596
+ const issueId = context.entityId;
597
+ const companyId = context.companyId;
598
+ const [showLinkInput, setShowLinkInput] = useState6(false);
599
+ const [selectedPrId, setSelectedPrId] = useState6(null);
600
+ const cardPRs = usePluginData5("card-prs", {
601
+ companyId,
602
+ issueId
603
+ });
604
+ const allPRs = usePluginData5("pull-requests", {
605
+ companyId,
606
+ filters: { state: "open" }
607
+ });
608
+ const linkAction = usePluginAction6("link-pr-to-card");
609
+ const quickCheck = usePluginAction6("run-quick-check");
610
+ if (!companyId || !issueId) {
611
+ return /* @__PURE__ */ jsx7("div", { style: { padding: "12px", fontSize: "13px", opacity: 0.5 }, children: "Sem contexto dispon\xEDvel." });
533
612
  }
534
- return /* @__PURE__ */ jsxs("div", { style: { padding: "1.5rem", display: "grid", gap: "1rem", maxWidth: 960 }, children: [
535
- /* @__PURE__ */ jsxs("header", { children: [
536
- /* @__PURE__ */ jsx("h1", { style: { margin: 0 }, children: "GitHub \u2014 Pull requests" }),
537
- /* @__PURE__ */ jsx("p", { style: { margin: "0.5rem 0 0", opacity: 0.8 }, children: "Sincronize PRs e issues abertos dos reposit\xF3rios rastreados." })
613
+ const prs = cardPRs.data?.pullRequests ?? [];
614
+ const handleLink = async () => {
615
+ if (!selectedPrId) return;
616
+ await linkAction({ prId: selectedPrId, issueId });
617
+ setShowLinkInput(false);
618
+ setSelectedPrId(null);
619
+ };
620
+ return /* @__PURE__ */ jsxs7("div", { style: { display: "flex", flexDirection: "column", gap: "8px", padding: "12px" }, children: [
621
+ prs.length === 0 ? /* @__PURE__ */ jsxs7("div", { style: { textAlign: "center", padding: "20px 0" }, children: [
622
+ /* @__PURE__ */ jsx7("p", { style: { fontSize: "13px", opacity: 0.5, margin: "0 0 12px" }, children: "Nenhum PR vinculado a este card." }),
623
+ /* @__PURE__ */ jsx7("button", { type: "button", style: buttonStyle, onClick: () => setShowLinkInput(true), children: "Vincular PR" })
624
+ ] }) : /* @__PURE__ */ jsxs7(Fragment, { children: [
625
+ prs.map((pr) => {
626
+ const badge = prStateBadge(pr.draft ? "draft" : pr.state);
627
+ return /* @__PURE__ */ jsx7("div", { style: cardStyle, children: /* @__PURE__ */ jsxs7("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start" }, children: [
628
+ /* @__PURE__ */ jsxs7("div", { style: { flex: 1 }, children: [
629
+ /* @__PURE__ */ jsxs7("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
630
+ /* @__PURE__ */ jsx7("span", { style: badgeStyle(badge.color), children: badge.label }),
631
+ /* @__PURE__ */ jsxs7("a", { href: pr.htmlUrl, target: "_blank", rel: "noopener", style: { fontWeight: 600, fontSize: "13px", color: "#3b82f6", textDecoration: "none" }, children: [
632
+ "#",
633
+ pr.number,
634
+ " ",
635
+ pr.title
636
+ ] })
637
+ ] }),
638
+ /* @__PURE__ */ jsxs7("div", { style: { marginTop: "4px", fontSize: "11px", opacity: 0.5 }, children: [
639
+ pr.repoFullName,
640
+ " \xB7 ",
641
+ pr.author,
642
+ " \xB7 ",
643
+ pr.headBranch,
644
+ " \u2192 ",
645
+ pr.baseBranch,
646
+ " \xB7 ",
647
+ timeAgo(pr.updatedAt)
648
+ ] })
649
+ ] }),
650
+ /* @__PURE__ */ jsx7(
651
+ ReviewDropdown,
652
+ {
653
+ companyId,
654
+ prId: pr.id,
655
+ repoFullName: pr.repoFullName,
656
+ prNumber: pr.number
657
+ }
658
+ )
659
+ ] }) }, pr.id);
660
+ }),
661
+ /* @__PURE__ */ jsx7("button", { type: "button", style: { ...buttonStyle, alignSelf: "flex-start", fontSize: "12px" }, onClick: () => setShowLinkInput(true), children: "+ Vincular outro PR" })
538
662
  ] }),
539
- needsToken ? /* @__PURE__ */ jsx("section", { style: panelStyle, children: /* @__PURE__ */ jsx("a", { ...nav.linkProps(PATHS.settings), children: "Configurar token GitHub" }) }) : null,
540
- /* @__PURE__ */ jsxs("section", { style: panelStyle, children: [
541
- /* @__PURE__ */ jsx("strong", { children: "Sync PRs / Issues" }),
542
- syncLoading ? /* @__PURE__ */ jsx("div", { children: "Carregando overview..." }) : null,
543
- syncError ? /* @__PURE__ */ jsxs("div", { children: [
544
- "Erro: ",
545
- syncError.message
546
- ] }) : null,
547
- syncOverview ? /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: "0.35rem" }, children: [
548
- /* @__PURE__ */ jsx("div", { children: syncOverview.message }),
549
- /* @__PURE__ */ jsxs("div", { children: [
550
- "PRs abertos: ",
551
- syncOverview.pullRequestCount,
552
- " \u2014 Issues abertas:",
553
- " ",
554
- syncOverview.issueCount
555
- ] }),
556
- syncOverview.lastSyncedAt ? /* @__PURE__ */ jsxs("div", { children: [
557
- "\xDAltimo sync: ",
558
- new Date(syncOverview.lastSyncedAt).toLocaleString()
559
- ] }) : null
560
- ] }) : null,
561
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: "0.5rem" }, children: [
562
- /* @__PURE__ */ jsx(
563
- "button",
564
- {
565
- style: buttonStyle,
566
- disabled: Boolean(busy) || !companyId || needsToken,
567
- onClick: () => void runAction("Sync completo", () => syncAll({ companyId })),
568
- children: "Sync tudo"
569
- }
570
- ),
571
- /* @__PURE__ */ jsx(
572
- "button",
573
- {
574
- style: buttonStyle,
575
- disabled: Boolean(busy) || !companyId || needsToken,
576
- onClick: () => void runAction("Sync PRs", () => syncPullRequests({ companyId })),
577
- children: "Sync PRs"
578
- }
579
- ),
580
- /* @__PURE__ */ jsx(
581
- "button",
582
- {
583
- style: buttonStyle,
584
- disabled: Boolean(busy) || !companyId || needsToken,
585
- onClick: () => void runAction("Sync issues", () => syncIssues({ companyId })),
586
- children: "Sync issues"
587
- }
588
- )
663
+ showLinkInput && /* @__PURE__ */ jsxs7("div", { style: cardStyle, children: [
664
+ /* @__PURE__ */ jsx7("div", { style: { fontSize: "13px", fontWeight: 500, marginBottom: "8px" }, children: "Selecionar PR" }),
665
+ /* @__PURE__ */ jsxs7(
666
+ "select",
667
+ {
668
+ style: { width: "100%", padding: "6px", borderRadius: "6px", border: "1px solid rgba(128,128,128,0.3)", background: "transparent", fontSize: "13px", marginBottom: "8px" },
669
+ onChange: (e) => setSelectedPrId(Number(e.target.value)),
670
+ value: selectedPrId ?? "",
671
+ children: [
672
+ /* @__PURE__ */ jsx7("option", { value: "", children: "Selecione um PR..." }),
673
+ (allPRs.data?.pullRequests ?? []).map((pr) => /* @__PURE__ */ jsxs7("option", { value: pr.id, children: [
674
+ pr.repoFullName,
675
+ " #",
676
+ pr.number,
677
+ " \u2014 ",
678
+ pr.title
679
+ ] }, pr.id))
680
+ ]
681
+ }
682
+ ),
683
+ /* @__PURE__ */ jsxs7("div", { style: { display: "flex", gap: "8px" }, children: [
684
+ /* @__PURE__ */ jsx7("button", { type: "button", style: buttonStyle, onClick: handleLink, disabled: !selectedPrId, children: "Vincular" }),
685
+ /* @__PURE__ */ jsx7("button", { type: "button", style: buttonStyle, onClick: () => setShowLinkInput(false), children: "Cancelar" })
686
+ ] })
687
+ ] })
688
+ ] });
689
+ }
690
+
691
+ // src/ui/components/DashboardWidget.tsx
692
+ import { useHostNavigation as useHostNavigation2, usePluginData as usePluginData6 } from "@paperclipai/plugin-sdk/ui";
693
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
694
+ function GitHubDashboardWidget({ context }) {
695
+ const nav = useHostNavigation2();
696
+ const syncStatus = usePluginData6("sync-status", {
697
+ companyId: context.companyId
698
+ });
699
+ const data = syncStatus.data;
700
+ return /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: "6px", fontSize: "12px" }, children: [
701
+ /* @__PURE__ */ jsxs8("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
702
+ /* @__PURE__ */ jsx8("strong", { style: { fontSize: "13px" }, children: "GitHub" }),
703
+ /* @__PURE__ */ jsx8("span", { style: {
704
+ display: "inline-block",
705
+ width: "8px",
706
+ height: "8px",
707
+ borderRadius: "50%",
708
+ background: data ? "#22c55e" : "#6b7280"
709
+ } })
710
+ ] }),
711
+ /* @__PURE__ */ jsxs8("div", { style: { display: "grid", gap: "2px" }, children: [
712
+ /* @__PURE__ */ jsxs8("div", { children: [
713
+ "Reposit\xF3rios: ",
714
+ data?.repoCount ?? 0
589
715
  ] }),
590
- syncOverview?.recentPullRequests.length ? /* @__PURE__ */ jsxs("div", { children: [
591
- /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "PRs recentes" }),
592
- /* @__PURE__ */ jsx("ul", { style: { margin: 0, paddingLeft: "1.2rem" }, children: syncOverview.recentPullRequests.map((pr) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", { href: pr.htmlUrl, target: "_blank", rel: "noreferrer", children: [
593
- pr.repoFullName,
594
- "#",
595
- pr.number,
596
- " \u2014 ",
597
- pr.title
598
- ] }) }, `${pr.repoFullName}#${pr.number}`)) })
599
- ] }) : null,
600
- syncOverview?.recentIssues.length ? /* @__PURE__ */ jsxs("div", { children: [
601
- /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Issues recentes" }),
602
- /* @__PURE__ */ jsx("ul", { style: { margin: 0, paddingLeft: "1.2rem" }, children: syncOverview.recentIssues.map((issue) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", { href: issue.htmlUrl, target: "_blank", rel: "noreferrer", children: [
603
- issue.repoFullName,
604
- "#",
605
- issue.number,
606
- " \u2014 ",
607
- issue.title
608
- ] }) }, `${issue.repoFullName}#${issue.number}`)) })
609
- ] }) : null
716
+ /* @__PURE__ */ jsxs8("div", { children: [
717
+ "PRs abertos: ",
718
+ data?.openPRCount ?? 0
719
+ ] }),
720
+ /* @__PURE__ */ jsxs8("div", { children: [
721
+ "\xDAltimo sync: ",
722
+ data?.lastSync ? timeAgo(data.lastSync) : "nunca"
723
+ ] })
610
724
  ] }),
611
- actionMessage ? /* @__PURE__ */ jsx("div", { children: actionMessage }) : null,
612
- busy ? /* @__PURE__ */ jsxs("div", { children: [
613
- "Executando: ",
614
- busy,
615
- "..."
616
- ] }) : null
725
+ /* @__PURE__ */ jsx8("a", { ...nav.linkProps(PATHS.prs), style: { fontSize: "12px", color: "#3b82f6" }, children: "Ver Pull Requests \u2192" })
617
726
  ] });
618
727
  }
728
+
729
+ // src/ui/components/ContextMenu.tsx
730
+ import { useHostContext as useHostContext5, usePluginAction as usePluginAction7 } from "@paperclipai/plugin-sdk/ui";
731
+ import { jsx as jsx9 } from "react/jsx-runtime";
732
+ function GitHubContextMenu() {
733
+ const context = useHostContext5();
734
+ const generateGraph = usePluginAction7("generate-graph");
735
+ const handleGraphify = () => {
736
+ if (!context.companyId) return;
737
+ void generateGraph({
738
+ companyId: context.companyId,
739
+ level: "high"
740
+ }).catch(console.error);
741
+ };
742
+ return /* @__PURE__ */ jsx9("button", { type: "button", style: buttonStyle, onClick: handleGraphify, children: "Gerar Knowledge Graph" });
743
+ }
619
744
  export {
620
- DashboardWidget,
745
+ GitHubContextMenu,
746
+ GitHubDashboardWidget,
747
+ GitHubDetailTab,
748
+ GitHubGraphsPage,
621
749
  GitHubPullRequestsPage,
622
750
  GitHubReposPage,
623
751
  GitHubRouteSidebar,
624
752
  GitHubSettingsPage,
625
- GitHubSidebarModule
753
+ GitHubSidebarLink,
754
+ GitHubSidebarPanel
626
755
  };
627
756
  //# sourceMappingURL=index.js.map