onflyt-cli 1.0.1-beta.2 → 1.0.1-beta.3
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.
Potentially problematic release.
This version of onflyt-cli might be problematic. Click here for more details.
- package/dist/App.d.ts +3 -0
- package/dist/App.js +8 -0
- package/dist/commands/credits.d.ts +3 -0
- package/dist/commands/credits.js +101 -0
- package/dist/commands/delete.d.ts +7 -0
- package/dist/commands/delete.js +220 -0
- package/dist/commands/deploy.d.ts +6 -0
- package/dist/commands/deploy.js +715 -0
- package/dist/commands/deployments.d.ts +6 -0
- package/dist/commands/deployments.js +225 -0
- package/dist/commands/help.d.ts +3 -0
- package/dist/commands/help.js +76 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.js +422 -0
- package/dist/commands/login.d.ts +6 -0
- package/dist/commands/login.js +150 -0
- package/dist/commands/logout.d.ts +3 -0
- package/dist/commands/logout.js +19 -0
- package/dist/commands/logs.d.ts +7 -0
- package/dist/commands/logs.js +307 -0
- package/dist/commands/projects.d.ts +3 -0
- package/dist/commands/projects.js +203 -0
- package/dist/commands/rollback.d.ts +6 -0
- package/dist/commands/rollback.js +316 -0
- package/dist/commands/teams.d.ts +3 -0
- package/dist/commands/teams.js +81 -0
- package/dist/commands/whoami.d.ts +3 -0
- package/dist/commands/whoami.js +34 -0
- package/dist/components/Loading.d.ts +13 -0
- package/dist/components/Loading.js +42 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +77 -116
- package/dist/lib/api.d.ts +27 -0
- package/dist/lib/api.js +109 -0
- package/dist/lib/config.d.ts +32 -0
- package/dist/lib/config.js +52 -0
- package/dist/lib/deploy-api.d.ts +97 -0
- package/dist/lib/deploy-api.js +335 -0
- package/dist/lib/deploy.d.ts +36 -0
- package/dist/lib/deploy.js +181 -0
- package/dist/lib/framework.d.ts +27 -0
- package/dist/lib/framework.js +184 -0
- package/dist/lib/git.d.ts +25 -0
- package/dist/lib/git.js +149 -0
- package/dist/lib/scaffold.d.ts +21 -0
- package/dist/lib/scaffold.js +190 -0
- package/dist/shared/frameworks/registry.d.ts +21 -0
- package/dist/shared/frameworks/registry.js +196 -0
- package/dist/shared/index.d.ts +4 -0
- package/dist/shared/index.js +4 -0
- package/dist/shared/limits.d.ts +16 -0
- package/dist/shared/limits.js +44 -0
- package/dist/shared/pricing.d.ts +2 -0
- package/dist/shared/pricing.js +7 -0
- package/dist/shared/templates/registry.d.ts +9 -0
- package/dist/shared/templates/registry.js +47 -0
- package/package.json +2 -3
- package/src/App.tsx +1 -1
- package/src/commands/deploy.tsx +1 -1
- package/src/commands/help.tsx +1 -1
- package/src/commands/init.tsx +1 -1
- package/src/commands/projects.tsx +1 -1
- package/src/components/Loading.tsx +1 -1
- package/src/index.tsx +1 -1
- package/src/lib/deploy-api.ts +3 -3
- package/src/lib/framework.ts +2 -2
- package/src/lib/shared.ts +350 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Text, Box, useInput } from "ink";
|
|
3
|
+
import { isLoggedIn, getConfig } from "../lib/config.js";
|
|
4
|
+
import { api } from "../lib/api.js";
|
|
5
|
+
import { Logo } from "../components/Loading.js";
|
|
6
|
+
import Spinner from "ink-spinner";
|
|
7
|
+
const Logs = ({ deploymentId, live = false }) => {
|
|
8
|
+
const [step, setStep] = useState("loading");
|
|
9
|
+
const [teams, setTeams] = useState([]);
|
|
10
|
+
const [projects, setProjects] = useState([]);
|
|
11
|
+
const [deployments, setDeployments] = useState([]);
|
|
12
|
+
const [selectedTeamIndex, setSelectedTeamIndex] = useState(0);
|
|
13
|
+
const [selectedProjectIndex, setSelectedProjectIndex] = useState(0);
|
|
14
|
+
const [selectedDeployIndex, setSelectedDeployIndex] = useState(0);
|
|
15
|
+
const [targetProject, setTargetProject] = useState(null);
|
|
16
|
+
const [targetDeploymentId, setTargetDeploymentId] = useState(null);
|
|
17
|
+
const [logs, setLogs] = useState([]);
|
|
18
|
+
const [errorMsg, setErrorMsg] = useState("");
|
|
19
|
+
const [dotCount, setDotCount] = useState(0);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const handleSigInt = () => process.exit(0);
|
|
22
|
+
process.on("SIGINT", handleSigInt);
|
|
23
|
+
return () => {
|
|
24
|
+
process.off("SIGINT", handleSigInt);
|
|
25
|
+
};
|
|
26
|
+
}, []);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (step === "done") {
|
|
29
|
+
const timer = setTimeout(() => process.exit(0), 500);
|
|
30
|
+
return () => clearTimeout(timer);
|
|
31
|
+
}
|
|
32
|
+
}, [step]);
|
|
33
|
+
useInput((input, key) => {
|
|
34
|
+
if (input === "q" || input === "Q" || (key.ctrl && input === "c")) {
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
if (step === "team") {
|
|
38
|
+
if (key.upArrow) {
|
|
39
|
+
setSelectedTeamIndex((i) => Math.max(0, i - 1));
|
|
40
|
+
}
|
|
41
|
+
else if (key.downArrow) {
|
|
42
|
+
setSelectedTeamIndex((i) => Math.min(teams.length - 1, i + 1));
|
|
43
|
+
}
|
|
44
|
+
else if (key.return) {
|
|
45
|
+
setStep("loading-projects");
|
|
46
|
+
loadProjects(teams[selectedTeamIndex].team.id);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (step === "project") {
|
|
50
|
+
if (key.upArrow) {
|
|
51
|
+
setSelectedProjectIndex((i) => Math.max(0, i - 1));
|
|
52
|
+
}
|
|
53
|
+
else if (key.downArrow) {
|
|
54
|
+
setSelectedProjectIndex((i) => Math.min(projects.length - 1, i + 1));
|
|
55
|
+
}
|
|
56
|
+
else if (key.return) {
|
|
57
|
+
setStep("loading-deployments");
|
|
58
|
+
loadDeployments(projects[selectedProjectIndex]);
|
|
59
|
+
}
|
|
60
|
+
else if (key.escape) {
|
|
61
|
+
setStep("team");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (step === "deploy") {
|
|
65
|
+
if (key.upArrow) {
|
|
66
|
+
setSelectedDeployIndex((i) => Math.max(0, i - 1));
|
|
67
|
+
}
|
|
68
|
+
else if (key.downArrow) {
|
|
69
|
+
setSelectedDeployIndex((i) => Math.min(deployments.length - 1, i + 1));
|
|
70
|
+
}
|
|
71
|
+
else if (key.return) {
|
|
72
|
+
const dep = deployments[selectedDeployIndex];
|
|
73
|
+
setTargetDeploymentId(dep.id);
|
|
74
|
+
setTargetProject(targetProject);
|
|
75
|
+
if (live) {
|
|
76
|
+
startLiveStream(dep.id);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
fetchStoredLogs(dep.id);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (key.escape) {
|
|
83
|
+
setStep("project");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (step !== "streaming")
|
|
89
|
+
return;
|
|
90
|
+
const interval = setInterval(() => {
|
|
91
|
+
setDotCount((c) => (c + 1) % 4);
|
|
92
|
+
}, 500);
|
|
93
|
+
return () => clearInterval(interval);
|
|
94
|
+
}, [step]);
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!isLoggedIn()) {
|
|
97
|
+
setErrorMsg("Not logged in. Run 'onflyt login' first.");
|
|
98
|
+
setStep("error");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (deploymentId) {
|
|
102
|
+
setTargetDeploymentId(deploymentId);
|
|
103
|
+
if (live) {
|
|
104
|
+
startLiveStream(deploymentId);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
fetchStoredLogs(deploymentId);
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
loadTeams();
|
|
112
|
+
}, []);
|
|
113
|
+
const loadTeams = async () => {
|
|
114
|
+
try {
|
|
115
|
+
const config = getConfig();
|
|
116
|
+
api.setToken(config.token);
|
|
117
|
+
const meData = await api.get("/auth/me");
|
|
118
|
+
const userTeams = meData.teams || [];
|
|
119
|
+
if (userTeams.length === 0) {
|
|
120
|
+
setErrorMsg("No teams found");
|
|
121
|
+
setStep("error");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
setTeams(userTeams);
|
|
125
|
+
if (userTeams.length === 1) {
|
|
126
|
+
setSelectedTeamIndex(0);
|
|
127
|
+
setStep("loading-projects");
|
|
128
|
+
loadProjects(userTeams[0].team.id);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
setStep("team");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
setErrorMsg(err.message);
|
|
136
|
+
setStep("error");
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const loadProjects = async (teamId) => {
|
|
140
|
+
try {
|
|
141
|
+
const projectsRes = await api.get(`/projects/team/${teamId}`);
|
|
142
|
+
const teamProjects = projectsRes.projects || [];
|
|
143
|
+
if (teamProjects.length === 0) {
|
|
144
|
+
setErrorMsg("No projects found in this team");
|
|
145
|
+
setStep("error");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
setProjects(teamProjects);
|
|
149
|
+
setSelectedProjectIndex(0);
|
|
150
|
+
setStep("project");
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
setErrorMsg(err.message);
|
|
154
|
+
setStep("error");
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const loadDeployments = async (project) => {
|
|
158
|
+
try {
|
|
159
|
+
setTargetProject(project);
|
|
160
|
+
const depsRes = await api.get(`/deployments/${project.id}?limit=50`);
|
|
161
|
+
const allDeployments = depsRes.deployments || [];
|
|
162
|
+
if (allDeployments.length === 0) {
|
|
163
|
+
setErrorMsg("No deployments found for this project");
|
|
164
|
+
setStep("error");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
setDeployments(allDeployments);
|
|
168
|
+
setSelectedDeployIndex(0);
|
|
169
|
+
setStep("deploy");
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
setErrorMsg(err.message);
|
|
173
|
+
setStep("error");
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const startLiveStream = (depId) => {
|
|
177
|
+
setStep("streaming");
|
|
178
|
+
setLogs([]);
|
|
179
|
+
import("../lib/deploy-api.js").then(({ streamLogs }) => {
|
|
180
|
+
const cancel = streamLogs(depId, (log) => {
|
|
181
|
+
setLogs((prev) => [...prev.slice(-500), log]);
|
|
182
|
+
}, () => {
|
|
183
|
+
console.log("\n\x1b[33m⚠ Live logs stream ended\x1b[0m");
|
|
184
|
+
setStep("done");
|
|
185
|
+
});
|
|
186
|
+
return () => {
|
|
187
|
+
cancel();
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
const fetchStoredLogs = (depId) => {
|
|
192
|
+
setStep("loading");
|
|
193
|
+
import("../lib/deploy-api.js").then(({ getStoredLogs }) => {
|
|
194
|
+
getStoredLogs(depId, { limit: 200 })
|
|
195
|
+
.then((result) => {
|
|
196
|
+
setLogs(result.logs.map((l) => `[${new Date(l.timestamp).toISOString()}] [${l.level.toUpperCase()}] ${l.message}`));
|
|
197
|
+
setStep("displaying");
|
|
198
|
+
})
|
|
199
|
+
.catch((err) => {
|
|
200
|
+
setErrorMsg(err.message || "Failed to fetch logs");
|
|
201
|
+
setStep("error");
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
if (step === "loading" ||
|
|
206
|
+
step === "loading-projects" ||
|
|
207
|
+
step === "loading-deployments") {
|
|
208
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
209
|
+
React.createElement(Logo, null),
|
|
210
|
+
React.createElement(Box, { marginTop: 1 },
|
|
211
|
+
React.createElement(Text, { bold: true }, "View Logs")),
|
|
212
|
+
React.createElement(Box, { marginTop: 1 },
|
|
213
|
+
React.createElement(Text, { dimColor: true },
|
|
214
|
+
"Loading",
|
|
215
|
+
step === "loading-projects"
|
|
216
|
+
? " projects..."
|
|
217
|
+
: step === "loading-deployments"
|
|
218
|
+
? " deployments..."
|
|
219
|
+
: "...")),
|
|
220
|
+
React.createElement(Box, { marginTop: 1 },
|
|
221
|
+
React.createElement(Text, null,
|
|
222
|
+
React.createElement(Spinner, null)))));
|
|
223
|
+
}
|
|
224
|
+
if (step === "error") {
|
|
225
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
226
|
+
React.createElement(Logo, null),
|
|
227
|
+
React.createElement(Box, { marginTop: 1 },
|
|
228
|
+
React.createElement(Text, { bold: true, color: "red" }, "\u2716 Error")),
|
|
229
|
+
React.createElement(Box, { marginTop: 1 },
|
|
230
|
+
React.createElement(Text, { color: "red" }, errorMsg))));
|
|
231
|
+
}
|
|
232
|
+
if (step === "team") {
|
|
233
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
234
|
+
React.createElement(Logo, null),
|
|
235
|
+
React.createElement(Box, { marginTop: 1 },
|
|
236
|
+
React.createElement(Text, { bold: true }, "View Logs")),
|
|
237
|
+
React.createElement(Box, { marginTop: 1 },
|
|
238
|
+
React.createElement(Text, { dimColor: true }, "Step 1/3: Select Team (\u2191\u2193 navigate, Enter select)")),
|
|
239
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" }, teams.map((t, idx) => (React.createElement(Box, { key: t.team.id, marginTop: 1 },
|
|
240
|
+
React.createElement(Text, { color: idx === selectedTeamIndex ? "cyan" : "gray" }, idx === selectedTeamIndex ? "▶ " : " "),
|
|
241
|
+
React.createElement(Text, { bold: idx === selectedTeamIndex }, t.team.name)))))));
|
|
242
|
+
}
|
|
243
|
+
if (step === "project") {
|
|
244
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
245
|
+
React.createElement(Logo, null),
|
|
246
|
+
React.createElement(Box, { marginTop: 1 },
|
|
247
|
+
React.createElement(Text, { bold: true }, "View Logs")),
|
|
248
|
+
React.createElement(Box, { marginTop: 1 },
|
|
249
|
+
React.createElement(Text, { dimColor: true },
|
|
250
|
+
"Step 2/3: Select Project - ",
|
|
251
|
+
teams[selectedTeamIndex]?.team.name)),
|
|
252
|
+
React.createElement(Box, null,
|
|
253
|
+
React.createElement(Text, { dimColor: true }, "(\u2191\u2193 navigate, Enter select, Esc go back)")),
|
|
254
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" }, projects.map((p, idx) => (React.createElement(Box, { key: p.id, marginTop: 1 },
|
|
255
|
+
React.createElement(Text, { color: idx === selectedProjectIndex ? "cyan" : "gray" }, idx === selectedProjectIndex ? "▶ " : " "),
|
|
256
|
+
React.createElement(Text, { bold: idx === selectedProjectIndex }, p.name)))))));
|
|
257
|
+
}
|
|
258
|
+
if (step === "deploy") {
|
|
259
|
+
const formatDate = (dateStr) => {
|
|
260
|
+
const date = new Date(dateStr);
|
|
261
|
+
return date.toLocaleDateString() + " " + date.toLocaleTimeString();
|
|
262
|
+
};
|
|
263
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
264
|
+
React.createElement(Logo, null),
|
|
265
|
+
React.createElement(Box, { marginTop: 1 },
|
|
266
|
+
React.createElement(Text, { bold: true }, "View Logs")),
|
|
267
|
+
React.createElement(Box, { marginTop: 1 },
|
|
268
|
+
React.createElement(Text, { dimColor: true },
|
|
269
|
+
"Step 3/3: Select Deployment - ",
|
|
270
|
+
targetProject?.name)),
|
|
271
|
+
React.createElement(Box, null,
|
|
272
|
+
React.createElement(Text, { dimColor: true }, "(\u2191\u2193 navigate, Enter select, Esc go back)")),
|
|
273
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" }, deployments.map((dep, idx) => (React.createElement(Box, { key: dep.id, marginTop: 1, flexDirection: "column" },
|
|
274
|
+
React.createElement(Box, null,
|
|
275
|
+
React.createElement(Text, { color: idx === selectedDeployIndex ? "cyan" : "gray" }, idx === selectedDeployIndex ? "▶ " : " "),
|
|
276
|
+
React.createElement(Text, { bold: idx === selectedDeployIndex }, dep.commitMessage || "Manual Upload"),
|
|
277
|
+
React.createElement(Text, { dimColor: true },
|
|
278
|
+
" [",
|
|
279
|
+
dep.status,
|
|
280
|
+
"]")),
|
|
281
|
+
React.createElement(Box, { marginLeft: 2 },
|
|
282
|
+
React.createElement(Text, { dimColor: true },
|
|
283
|
+
"ID: ",
|
|
284
|
+
dep.id)),
|
|
285
|
+
React.createElement(Box, { marginLeft: 2 },
|
|
286
|
+
React.createElement(Text, { dimColor: true }, formatDate(dep.createdAt)))))))));
|
|
287
|
+
}
|
|
288
|
+
const dots = ".".repeat(dotCount);
|
|
289
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
290
|
+
React.createElement(Logo, null),
|
|
291
|
+
React.createElement(Box, { marginTop: 1 },
|
|
292
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Deployment:"),
|
|
293
|
+
React.createElement(Text, null,
|
|
294
|
+
" ",
|
|
295
|
+
targetDeploymentId),
|
|
296
|
+
step === "streaming" && React.createElement(Text, { dimColor: true },
|
|
297
|
+
" ",
|
|
298
|
+
dots),
|
|
299
|
+
step === "streaming" && (React.createElement(Box, { marginLeft: 2 },
|
|
300
|
+
React.createElement(Text, { dimColor: true }, "(Ctrl+C to exit)")))),
|
|
301
|
+
React.createElement(Box, { marginTop: 1 },
|
|
302
|
+
React.createElement(Text, { dimColor: true }, live
|
|
303
|
+
? "Streaming live logs..."
|
|
304
|
+
: `Showing last ${logs.length} log entries`)),
|
|
305
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column", borderStyle: "round", borderDimColor: true, paddingX: 1 }, logs.length === 0 ? (React.createElement(Text, { dimColor: true }, "No logs available")) : (logs.map((log, idx) => (React.createElement(Text, { key: idx, wrap: "wrap" }, log)))))));
|
|
306
|
+
};
|
|
307
|
+
export default Logs;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Text, Box, useInput } from "ink";
|
|
3
|
+
import { isLoggedIn, getConfig } from "../lib/config.js";
|
|
4
|
+
import { api } from "../lib/api.js";
|
|
5
|
+
import { Logo } from "../components/Loading.js";
|
|
6
|
+
import Spinner from "ink-spinner";
|
|
7
|
+
import { FRAMEWORKS } from "../shared";
|
|
8
|
+
const ProjectsList = () => {
|
|
9
|
+
const [step, setStep] = useState("loading");
|
|
10
|
+
const [teams, setTeams] = useState([]);
|
|
11
|
+
const [projects, setProjects] = useState([]);
|
|
12
|
+
const [selectedTeamIndex, setSelectedTeamIndex] = useState(0);
|
|
13
|
+
const [selectedProjectIndex, setSelectedProjectIndex] = useState(0);
|
|
14
|
+
const [errorMsg, setErrorMsg] = useState("");
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const handleSigInt = () => process.exit(0);
|
|
17
|
+
process.on("SIGINT", handleSigInt);
|
|
18
|
+
return () => {
|
|
19
|
+
process.off("SIGINT", handleSigInt);
|
|
20
|
+
};
|
|
21
|
+
}, []);
|
|
22
|
+
useInput((input, key) => {
|
|
23
|
+
if (input === "q" || input === "Q" || (key.ctrl && input === "c")) {
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
if (step === "team") {
|
|
27
|
+
if (key.upArrow) {
|
|
28
|
+
setSelectedTeamIndex((i) => Math.max(0, i - 1));
|
|
29
|
+
}
|
|
30
|
+
else if (key.downArrow) {
|
|
31
|
+
setSelectedTeamIndex((i) => Math.min(teams.length - 1, i + 1));
|
|
32
|
+
}
|
|
33
|
+
else if (key.return) {
|
|
34
|
+
setStep("loading-projects");
|
|
35
|
+
loadProjects(teams[selectedTeamIndex].team.id);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (step === "project") {
|
|
39
|
+
if (key.upArrow) {
|
|
40
|
+
setSelectedProjectIndex((i) => Math.max(0, i - 1));
|
|
41
|
+
}
|
|
42
|
+
else if (key.downArrow) {
|
|
43
|
+
setSelectedProjectIndex((i) => Math.min(projects.length - 1, i + 1));
|
|
44
|
+
}
|
|
45
|
+
else if (key.return) {
|
|
46
|
+
setStep("display");
|
|
47
|
+
}
|
|
48
|
+
else if (key.escape) {
|
|
49
|
+
setStep("team");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!isLoggedIn()) {
|
|
55
|
+
setErrorMsg("Not logged in. Run 'onflyt login' first.");
|
|
56
|
+
setStep("error");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
loadTeams();
|
|
60
|
+
}, []);
|
|
61
|
+
const loadTeams = async () => {
|
|
62
|
+
try {
|
|
63
|
+
const config = getConfig();
|
|
64
|
+
api.setToken(config.token);
|
|
65
|
+
const meData = await api.get("/auth/me");
|
|
66
|
+
const userTeams = meData.teams || [];
|
|
67
|
+
if (userTeams.length === 0) {
|
|
68
|
+
setErrorMsg("No teams found");
|
|
69
|
+
setStep("error");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
setTeams(userTeams);
|
|
73
|
+
if (userTeams.length === 1) {
|
|
74
|
+
setSelectedTeamIndex(0);
|
|
75
|
+
setStep("loading-projects");
|
|
76
|
+
loadProjects(userTeams[0].team.id);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
setStep("team");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
setErrorMsg(err.message);
|
|
84
|
+
setStep("error");
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const loadProjects = async (teamId) => {
|
|
88
|
+
try {
|
|
89
|
+
const projectsRes = await api.get(`/projects/team/${teamId}`);
|
|
90
|
+
const teamProjects = projectsRes.projects || [];
|
|
91
|
+
setProjects(teamProjects);
|
|
92
|
+
setSelectedProjectIndex(0);
|
|
93
|
+
setStep("project");
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
setErrorMsg(err.message);
|
|
97
|
+
setStep("error");
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
if (step === "loading" || step === "loading-projects") {
|
|
101
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
102
|
+
React.createElement(Logo, null),
|
|
103
|
+
React.createElement(Box, { marginTop: 1 },
|
|
104
|
+
React.createElement(Text, { bold: true }, "Projects")),
|
|
105
|
+
React.createElement(Box, { marginTop: 1 },
|
|
106
|
+
React.createElement(Text, { dimColor: true },
|
|
107
|
+
"Loading",
|
|
108
|
+
step === "loading-projects" ? " projects..." : "...")),
|
|
109
|
+
React.createElement(Box, { marginTop: 1 },
|
|
110
|
+
React.createElement(Text, null,
|
|
111
|
+
React.createElement(Spinner, null)))));
|
|
112
|
+
}
|
|
113
|
+
if (step === "error") {
|
|
114
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
115
|
+
React.createElement(Logo, null),
|
|
116
|
+
React.createElement(Box, { marginTop: 1 },
|
|
117
|
+
React.createElement(Text, { bold: true, color: "red" }, "\u2716 Error")),
|
|
118
|
+
React.createElement(Box, { marginTop: 1 },
|
|
119
|
+
React.createElement(Text, { color: "red" }, errorMsg))));
|
|
120
|
+
}
|
|
121
|
+
if (step === "team") {
|
|
122
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
123
|
+
React.createElement(Logo, null),
|
|
124
|
+
React.createElement(Box, { marginTop: 1 },
|
|
125
|
+
React.createElement(Text, { bold: true }, "Projects")),
|
|
126
|
+
React.createElement(Box, { marginTop: 1 },
|
|
127
|
+
React.createElement(Text, { dimColor: true }, "Step 1/2: Select Team (\u2191\u2193 navigate, Enter select)")),
|
|
128
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" }, teams.map((t, idx) => (React.createElement(Box, { key: t.team.id, marginTop: 1 },
|
|
129
|
+
React.createElement(Text, { color: idx === selectedTeamIndex ? "cyan" : "gray" }, idx === selectedTeamIndex ? "▶ " : " "),
|
|
130
|
+
React.createElement(Text, { bold: idx === selectedTeamIndex }, t.team.name)))))));
|
|
131
|
+
}
|
|
132
|
+
if (step === "project") {
|
|
133
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
134
|
+
React.createElement(Logo, null),
|
|
135
|
+
React.createElement(Box, { marginTop: 1 },
|
|
136
|
+
React.createElement(Text, { bold: true }, "Projects")),
|
|
137
|
+
React.createElement(Box, { marginTop: 1 },
|
|
138
|
+
React.createElement(Text, { dimColor: true },
|
|
139
|
+
"Step 2/2: Select Project - ",
|
|
140
|
+
teams[selectedTeamIndex]?.team.name)),
|
|
141
|
+
React.createElement(Box, null,
|
|
142
|
+
React.createElement(Text, { dimColor: true }, "(\u2191\u2193 navigate, Enter view details, Esc go back)")),
|
|
143
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" }, projects.length === 0 ? (React.createElement(Text, { dimColor: true }, "No projects in this team")) : (projects.map((p, idx) => (React.createElement(Box, { key: p.id, marginTop: 1 },
|
|
144
|
+
React.createElement(Text, { color: idx === selectedProjectIndex ? "cyan" : "gray" }, idx === selectedProjectIndex ? "▶ " : " "),
|
|
145
|
+
React.createElement(Text, { bold: idx === selectedProjectIndex }, p.name),
|
|
146
|
+
React.createElement(Text, { dimColor: true },
|
|
147
|
+
" ",
|
|
148
|
+
"(",
|
|
149
|
+
FRAMEWORKS[p.framework]?.label || p.framework,
|
|
150
|
+
")"),
|
|
151
|
+
React.createElement(Text, { dimColor: true },
|
|
152
|
+
" ",
|
|
153
|
+
"- ",
|
|
154
|
+
p.status === "active" ? "●" : "○",
|
|
155
|
+
" ",
|
|
156
|
+
p.status))))))));
|
|
157
|
+
}
|
|
158
|
+
const selectedProject = projects[selectedProjectIndex];
|
|
159
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
160
|
+
React.createElement(Logo, null),
|
|
161
|
+
React.createElement(Box, { marginTop: 1 },
|
|
162
|
+
React.createElement(Text, { bold: true }, "Project Details")),
|
|
163
|
+
React.createElement(Box, null,
|
|
164
|
+
React.createElement(Text, { dimColor: true },
|
|
165
|
+
teams[selectedTeamIndex]?.team.name,
|
|
166
|
+
" / ",
|
|
167
|
+
selectedProject?.name)),
|
|
168
|
+
selectedProject && (React.createElement(Box, { marginTop: 1, flexDirection: "column", borderStyle: "round", borderDimColor: true, paddingX: 1 },
|
|
169
|
+
React.createElement(Box, { marginTop: 1 },
|
|
170
|
+
React.createElement(Text, { bold: true }, "Name:"),
|
|
171
|
+
React.createElement(Text, null,
|
|
172
|
+
" ",
|
|
173
|
+
selectedProject.name)),
|
|
174
|
+
React.createElement(Box, { marginTop: 1 },
|
|
175
|
+
React.createElement(Text, { bold: true }, "Framework:"),
|
|
176
|
+
React.createElement(Text, null,
|
|
177
|
+
" ",
|
|
178
|
+
FRAMEWORKS[selectedProject.framework]?.label ||
|
|
179
|
+
selectedProject.framework)),
|
|
180
|
+
React.createElement(Box, { marginTop: 1 },
|
|
181
|
+
React.createElement(Text, { bold: true }, "Status:"),
|
|
182
|
+
React.createElement(Text, null,
|
|
183
|
+
" ",
|
|
184
|
+
selectedProject.status)),
|
|
185
|
+
React.createElement(Box, { marginTop: 1 },
|
|
186
|
+
React.createElement(Text, { bold: true }, "ID:"),
|
|
187
|
+
React.createElement(Text, { dimColor: true },
|
|
188
|
+
" ",
|
|
189
|
+
selectedProject.id)),
|
|
190
|
+
selectedProject.gitRepoUrl && (React.createElement(Box, { marginTop: 1 },
|
|
191
|
+
React.createElement(Text, { bold: true }, "Repository:"),
|
|
192
|
+
React.createElement(Text, { color: "cyan" },
|
|
193
|
+
" ",
|
|
194
|
+
selectedProject.gitRepoUrl))),
|
|
195
|
+
selectedProject.outputDirectory && (React.createElement(Box, { marginTop: 1 },
|
|
196
|
+
React.createElement(Text, { bold: true }, "Output:"),
|
|
197
|
+
React.createElement(Text, { dimColor: true },
|
|
198
|
+
" ",
|
|
199
|
+
selectedProject.outputDirectory))))),
|
|
200
|
+
React.createElement(Box, { marginTop: 1 },
|
|
201
|
+
React.createElement(Text, { dimColor: true }, "\u2191\u2193 navigate projects | Esc go back"))));
|
|
202
|
+
};
|
|
203
|
+
export default ProjectsList;
|