@tscircuit/fake-snippets 0.0.100 → 0.0.102
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/api/generated-index.js +23 -1
- package/bun.lock +2 -2
- package/dist/bundle.js +620 -412
- package/dist/index.d.ts +33 -4
- package/dist/index.js +43 -1
- package/dist/schema.d.ts +94 -1
- package/dist/schema.js +17 -1
- package/fake-snippets-api/lib/db/db-client.ts +38 -1
- package/fake-snippets-api/lib/db/schema.ts +15 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package.ts +2 -0
- package/fake-snippets-api/routes/api/accounts/search.ts +20 -0
- package/fake-snippets-api/routes/api/github/installations/create_new_installation_redirect.ts +75 -0
- package/fake-snippets-api/routes/api/github/repos/list_available.ts +91 -0
- package/fake-snippets-api/routes/api/packages/update.ts +4 -0
- package/package.json +2 -2
- package/src/App.tsx +10 -1
- package/src/components/CmdKMenu.tsx +154 -19
- package/src/components/CreateReleaseDialog.tsx +124 -0
- package/src/components/FileSidebar.tsx +128 -23
- package/src/components/Header2.tsx +106 -25
- package/src/components/PackageBuildsPage/package-build-header.tsx +28 -16
- package/src/components/PageSearchComponent.tsx +2 -2
- package/src/components/SearchComponent.tsx +2 -2
- package/src/components/SuspenseRunFrame.tsx +2 -2
- package/src/components/TrendingPackagesCarousel.tsx +2 -2
- package/src/components/ViewPackagePage/components/important-files-view.tsx +18 -13
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -0
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +1 -0
- package/src/components/dialogs/GitHubRepositorySelector.tsx +123 -0
- package/src/components/dialogs/create-use-dialog.tsx +8 -2
- package/src/components/dialogs/edit-package-details-dialog.tsx +22 -3
- package/src/components/dialogs/view-ts-files-dialog.tsx +178 -33
- package/src/components/package-port/CodeAndPreview.tsx +4 -1
- package/src/components/package-port/CodeEditor.tsx +42 -35
- package/src/components/package-port/CodeEditorHeader.tsx +26 -20
- package/src/components/package-port/EditorNav.tsx +94 -37
- package/src/components/preview/BuildsList.tsx +238 -0
- package/src/components/preview/ConnectedRepoDashboard.tsx +258 -0
- package/src/components/preview/ConnectedRepoOverview.tsx +454 -0
- package/src/components/preview/ConnectedRepoSettings.tsx +343 -0
- package/src/components/preview/ConnectedReposCards.tsx +191 -0
- package/src/components/preview/index.tsx +207 -0
- package/src/components/ui/tree-view.tsx +23 -6
- package/src/hooks/use-axios.ts +2 -2
- package/src/hooks/use-create-release-dialog.ts +160 -0
- package/src/hooks/use-package-details-form.ts +7 -0
- package/src/hooks/use-packages-base-api-url.ts +1 -1
- package/src/hooks/use-sign-in.ts +2 -2
- package/src/hooks/useFileManagement.ts +22 -2
- package/src/index.css +4 -0
- package/src/lib/utils/formatTimeAgo.ts +10 -0
- package/src/lib/utils/isValidFileName.ts +15 -3
- package/src/pages/dashboard.tsx +2 -2
- package/src/pages/dev-login.tsx +2 -2
- package/src/pages/landing.tsx +1 -1
- package/src/pages/latest.tsx +2 -2
- package/src/pages/preview-build.tsx +380 -0
- package/src/pages/search.tsx +2 -2
- package/src/pages/trending.tsx +2 -2
- package/src/pages/user-profile.tsx +32 -24
- package/src/pages/view-connected-repo.tsx +24 -0
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
} from "@/components/ui/tooltip"
|
|
28
28
|
import { convertRawEasyToTsx, fetchEasyEDAComponent } from "easyeda/browser"
|
|
29
29
|
import { ComponentSearchResult } from "@tscircuit/runframe/runner"
|
|
30
|
-
import {
|
|
30
|
+
import { useApiBaseUrl } from "@/hooks/use-packages-base-api-url"
|
|
31
31
|
import { ICreateFileProps, ICreateFileResult } from "@/hooks/useFileManagement"
|
|
32
32
|
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
33
33
|
|
|
@@ -60,7 +60,7 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
|
|
|
60
60
|
useImportComponentDialog()
|
|
61
61
|
const { toast, toastLibrary } = useToast()
|
|
62
62
|
const [sidebarOpen, setSidebarOpen] = fileSidebarState
|
|
63
|
-
const API_BASE =
|
|
63
|
+
const API_BASE = useApiBaseUrl()
|
|
64
64
|
const [aiAutocompleteEnabled, setAiAutocompleteEnabled] = aiAutocompleteState
|
|
65
65
|
const session = useGlobalStore((s) => s.session)
|
|
66
66
|
|
|
@@ -162,29 +162,35 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
|
|
|
162
162
|
if (!session?.token) {
|
|
163
163
|
throw new Error("You need to be logged in to import jlcpcb component")
|
|
164
164
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
165
|
+
|
|
166
|
+
const jlcpcbComponent = await fetchEasyEDAComponent(
|
|
167
|
+
component.partNumber ?? component.name,
|
|
168
|
+
{
|
|
169
|
+
fetch: ((url, options: any) => {
|
|
170
|
+
return fetch(`${API_BASE}/proxy`, {
|
|
171
|
+
body: options.body,
|
|
172
|
+
method: options.method,
|
|
173
|
+
headers: {
|
|
174
|
+
authority: options.headers.authority,
|
|
175
|
+
Authorization: `Bearer ${session?.token}`,
|
|
176
|
+
"X-Target-Url": url.toString(),
|
|
177
|
+
"X-Sender-Host": options.headers.origin,
|
|
178
|
+
"X-Sender-Origin": options.headers.origin,
|
|
179
|
+
"content-type": options.headers["content-type"],
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
}) as typeof fetch,
|
|
183
|
+
},
|
|
184
|
+
)
|
|
181
185
|
const tsxComponent = await convertRawEasyToTsx(jlcpcbComponent)
|
|
182
186
|
let componentName = component.name.replace(/ /g, "-")
|
|
183
|
-
|
|
187
|
+
let componentPath = `imports/${componentName}.tsx`
|
|
188
|
+
if (files[componentPath] || files[`./${componentPath}`]) {
|
|
184
189
|
componentName = `${componentName}-1`
|
|
190
|
+
componentPath = `imports/${componentName}.tsx`
|
|
185
191
|
}
|
|
186
192
|
const createFileResult = createFile({
|
|
187
|
-
newFileName:
|
|
193
|
+
newFileName: componentPath,
|
|
188
194
|
content: tsxComponent,
|
|
189
195
|
onError: (error) => {
|
|
190
196
|
throw error
|
|
@@ -45,6 +45,9 @@ import { useForkPackageMutation } from "@/hooks/useForkPackageMutation"
|
|
|
45
45
|
import tscircuitCorePkg from "@tscircuit/core/package.json"
|
|
46
46
|
import { useRenamePackageDialog } from "../dialogs/rename-package-dialog"
|
|
47
47
|
import { useUpdatePackageDescriptionDialog } from "../dialogs/update-package-description-dialog"
|
|
48
|
+
import { useCreateReleaseDialog } from "@/hooks/use-create-release-dialog"
|
|
49
|
+
import { Tag } from "lucide-react"
|
|
50
|
+
import { CreateReleaseDialog } from "../CreateReleaseDialog"
|
|
48
51
|
|
|
49
52
|
export default function EditorNav({
|
|
50
53
|
circuitJson,
|
|
@@ -58,6 +61,8 @@ export default function EditorNav({
|
|
|
58
61
|
onDiscard,
|
|
59
62
|
packageType,
|
|
60
63
|
isSaving,
|
|
64
|
+
files,
|
|
65
|
+
packageFilesMeta,
|
|
61
66
|
}: {
|
|
62
67
|
pkg?: Package | null
|
|
63
68
|
circuitJson?: AnyCircuitElement[] | null
|
|
@@ -70,6 +75,13 @@ export default function EditorNav({
|
|
|
70
75
|
isSaving: boolean
|
|
71
76
|
onSave: () => void
|
|
72
77
|
onDiscard?: () => void
|
|
78
|
+
files?: { path: string; content: string }[]
|
|
79
|
+
packageFilesMeta?: {
|
|
80
|
+
created_at: string
|
|
81
|
+
file_path: string
|
|
82
|
+
package_file_id: string
|
|
83
|
+
package_release_id: string
|
|
84
|
+
}[]
|
|
73
85
|
}) {
|
|
74
86
|
const [, navigate] = useLocation()
|
|
75
87
|
const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
|
|
@@ -84,6 +96,18 @@ export default function EditorNav({
|
|
|
84
96
|
useConfirmDeletePackageDialog()
|
|
85
97
|
const { Dialog: ViewTsFilesDialog, openDialog: openViewTsFilesDialog } =
|
|
86
98
|
useViewTsFilesDialog()
|
|
99
|
+
const createReleaseDialog = useCreateReleaseDialog({
|
|
100
|
+
packageId: pkg?.package_id ?? "",
|
|
101
|
+
packageName: pkg?.unscoped_name ?? "",
|
|
102
|
+
currentVersion: pkg?.latest_version || undefined,
|
|
103
|
+
onSuccess: () => {
|
|
104
|
+
qc.invalidateQueries({ queryKey: ["packages"] })
|
|
105
|
+
qc.invalidateQueries({ queryKey: ["packages", pkg?.package_id] })
|
|
106
|
+
},
|
|
107
|
+
files: files || [],
|
|
108
|
+
currentPackage: pkg || undefined,
|
|
109
|
+
packageFilesMeta: packageFilesMeta || [],
|
|
110
|
+
})
|
|
87
111
|
|
|
88
112
|
const [isChangingType, setIsChangingType] = useState(false)
|
|
89
113
|
const [currentType, setCurrentType] = useState(
|
|
@@ -229,14 +253,25 @@ export default function EditorNav({
|
|
|
229
253
|
</span>
|
|
230
254
|
)}
|
|
231
255
|
{pkg.owner_github_username === session?.github_username && (
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
256
|
+
<>
|
|
257
|
+
<Button
|
|
258
|
+
variant="ghost"
|
|
259
|
+
size="icon"
|
|
260
|
+
className="h-6 w-6 ml-2"
|
|
261
|
+
onClick={() => openRenameDialog()}
|
|
262
|
+
>
|
|
263
|
+
<Pencil className="h-3 w-3 text-gray-700" />
|
|
264
|
+
</Button>
|
|
265
|
+
<Button
|
|
266
|
+
variant="ghost"
|
|
267
|
+
size="icon"
|
|
268
|
+
className="h-6 w-6 ml-2"
|
|
269
|
+
onClick={() => createReleaseDialog.openDialog()}
|
|
270
|
+
disabled={hasUnsavedChanges || isSaving}
|
|
271
|
+
>
|
|
272
|
+
<Tag className="h-3 w-3 text-gray-700" />
|
|
273
|
+
</Button>
|
|
274
|
+
</>
|
|
240
275
|
)}
|
|
241
276
|
{isPrivate && (
|
|
242
277
|
<div className="relative group">
|
|
@@ -363,22 +398,23 @@ export default function EditorNav({
|
|
|
363
398
|
<Eye className="mr-1 h-3 w-3" />
|
|
364
399
|
Public
|
|
365
400
|
</Button> */}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
401
|
+
|
|
402
|
+
<DropdownMenu>
|
|
403
|
+
<DropdownMenuTrigger asChild>
|
|
404
|
+
<Button variant="ghost" size="icon" className="hidden md:flex">
|
|
405
|
+
<MoreVertical className="h-3 w-3" />
|
|
406
|
+
</Button>
|
|
407
|
+
</DropdownMenuTrigger>
|
|
408
|
+
<DropdownMenuContent>
|
|
409
|
+
<DropdownMenuItem
|
|
410
|
+
className="text-xs"
|
|
411
|
+
onClick={() => openViewTsFilesDialog()}
|
|
412
|
+
>
|
|
413
|
+
<File className="mr-2 h-3 w-3" />
|
|
414
|
+
View Files
|
|
415
|
+
</DropdownMenuItem>
|
|
416
|
+
{pkg &&
|
|
417
|
+
session?.github_username === pkg?.owner_github_username && (
|
|
382
418
|
<>
|
|
383
419
|
<DropdownMenuItem
|
|
384
420
|
className="text-xs"
|
|
@@ -436,21 +472,21 @@ export default function EditorNav({
|
|
|
436
472
|
</DropdownMenuItem>
|
|
437
473
|
</DropdownMenuSubContent>
|
|
438
474
|
</DropdownMenuSub>
|
|
475
|
+
<DropdownMenuItem
|
|
476
|
+
className="text-xs text-red-600"
|
|
477
|
+
onClick={() => openDeleteDialog()}
|
|
478
|
+
>
|
|
479
|
+
<Trash2 className="mr-2 h-3 w-3" />
|
|
480
|
+
Delete Package
|
|
481
|
+
</DropdownMenuItem>
|
|
439
482
|
</>
|
|
440
483
|
)}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
</DropdownMenuItem>
|
|
448
|
-
<DropdownMenuItem className="text-xs text-gray-500" disabled>
|
|
449
|
-
@tscircuit/core@{tscircuitCorePkg.version}
|
|
450
|
-
</DropdownMenuItem>
|
|
451
|
-
</DropdownMenuContent>
|
|
452
|
-
</DropdownMenu>
|
|
453
|
-
)}
|
|
484
|
+
<DropdownMenuItem className="text-xs text-gray-500" disabled>
|
|
485
|
+
@tscircuit/core@{tscircuitCorePkg.version}
|
|
486
|
+
</DropdownMenuItem>
|
|
487
|
+
</DropdownMenuContent>
|
|
488
|
+
</DropdownMenu>
|
|
489
|
+
|
|
454
490
|
<Button
|
|
455
491
|
variant="ghost"
|
|
456
492
|
size="icon"
|
|
@@ -488,6 +524,17 @@ export default function EditorNav({
|
|
|
488
524
|
Discard Changes
|
|
489
525
|
</DropdownMenuItem>
|
|
490
526
|
)}
|
|
527
|
+
{pkg &&
|
|
528
|
+
session?.github_username === pkg?.owner_github_username && (
|
|
529
|
+
<DropdownMenuItem
|
|
530
|
+
className="text-xs"
|
|
531
|
+
onClick={() => createReleaseDialog.openDialog()}
|
|
532
|
+
disabled={hasUnsavedChanges || isSaving}
|
|
533
|
+
>
|
|
534
|
+
<Tag className="mr-1 h-3 w-3" />
|
|
535
|
+
Create Release
|
|
536
|
+
</DropdownMenuItem>
|
|
537
|
+
)}
|
|
491
538
|
<DropdownMenuItem
|
|
492
539
|
className="text-xs"
|
|
493
540
|
onClick={() => {
|
|
@@ -561,6 +608,16 @@ export default function EditorNav({
|
|
|
561
608
|
packageOwner={pkg?.owner_github_username ?? ""}
|
|
562
609
|
/>
|
|
563
610
|
<ViewTsFilesDialog />
|
|
611
|
+
<CreateReleaseDialog
|
|
612
|
+
isOpen={createReleaseDialog.isOpen}
|
|
613
|
+
onClose={createReleaseDialog.closeDialog}
|
|
614
|
+
currentVersion={createReleaseDialog.currentVersion}
|
|
615
|
+
version={createReleaseDialog.version}
|
|
616
|
+
setVersion={createReleaseDialog.setVersion}
|
|
617
|
+
isLoading={createReleaseDialog.isLoading}
|
|
618
|
+
error={createReleaseDialog.error}
|
|
619
|
+
onCreateRelease={createReleaseDialog.handleCreateRelease}
|
|
620
|
+
/>
|
|
564
621
|
</nav>
|
|
565
622
|
)
|
|
566
623
|
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
2
|
+
import { Badge } from "@/components/ui/badge"
|
|
3
|
+
import { Button } from "@/components/ui/button"
|
|
4
|
+
import {
|
|
5
|
+
Clock,
|
|
6
|
+
GitBranch,
|
|
7
|
+
AlertCircle,
|
|
8
|
+
CheckCircle,
|
|
9
|
+
Loader2,
|
|
10
|
+
MoreHorizontal,
|
|
11
|
+
GitCommit,
|
|
12
|
+
Plus,
|
|
13
|
+
} from "lucide-react"
|
|
14
|
+
import {
|
|
15
|
+
DropdownMenu,
|
|
16
|
+
DropdownMenuContent,
|
|
17
|
+
DropdownMenuItem,
|
|
18
|
+
DropdownMenuTrigger,
|
|
19
|
+
} from "@/components/ui/dropdown-menu"
|
|
20
|
+
import {
|
|
21
|
+
Table,
|
|
22
|
+
TableBody,
|
|
23
|
+
TableCell,
|
|
24
|
+
TableHead,
|
|
25
|
+
TableHeader,
|
|
26
|
+
TableRow,
|
|
27
|
+
} from "@/components/ui/table"
|
|
28
|
+
import { getBuildStatus, PackageBuild, StatusIcon } from "."
|
|
29
|
+
import { formatTimeAgo } from "@/lib/utils/formatTimeAgo"
|
|
30
|
+
|
|
31
|
+
interface BuildsListProps {
|
|
32
|
+
builds: PackageBuild[]
|
|
33
|
+
onSelectBuild?: (build: PackageBuild) => void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const BuildsList = ({ builds, onSelectBuild }: BuildsListProps) => {
|
|
37
|
+
return (
|
|
38
|
+
<div className="space-y-6">
|
|
39
|
+
<div className="flex items-center justify-between">
|
|
40
|
+
<div>
|
|
41
|
+
<h2 className="text-2xl font-bold text-gray-900">Builds</h2>
|
|
42
|
+
<p className="text-gray-600">Manage and monitor your builds</p>
|
|
43
|
+
</div>
|
|
44
|
+
<Button className="flex items-center gap-2">
|
|
45
|
+
<Plus className="w-4 h-4" />
|
|
46
|
+
Create Build
|
|
47
|
+
</Button>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<Card>
|
|
51
|
+
<CardHeader>
|
|
52
|
+
<CardTitle>Recent Deployments</CardTitle>
|
|
53
|
+
</CardHeader>
|
|
54
|
+
<CardContent>
|
|
55
|
+
<div className="overflow-x-auto">
|
|
56
|
+
<Table>
|
|
57
|
+
<TableHeader>
|
|
58
|
+
<TableRow>
|
|
59
|
+
<TableHead>Status</TableHead>
|
|
60
|
+
<TableHead>Build ID</TableHead>
|
|
61
|
+
<TableHead>Branch</TableHead>
|
|
62
|
+
<TableHead>Commit</TableHead>
|
|
63
|
+
<TableHead>Author</TableHead>
|
|
64
|
+
<TableHead>Created</TableHead>
|
|
65
|
+
<TableHead>Actions</TableHead>
|
|
66
|
+
</TableRow>
|
|
67
|
+
</TableHeader>
|
|
68
|
+
<TableBody>
|
|
69
|
+
{builds.map((build) => {
|
|
70
|
+
const { status, label } = getBuildStatus(build)
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<TableRow
|
|
74
|
+
key={build.package_build_id}
|
|
75
|
+
className="cursor-pointer hover:bg-gray-50"
|
|
76
|
+
onClick={() => onSelectBuild?.(build)}
|
|
77
|
+
>
|
|
78
|
+
<TableCell>
|
|
79
|
+
<div className="flex items-center gap-2">
|
|
80
|
+
<StatusIcon status={status} />
|
|
81
|
+
<Badge
|
|
82
|
+
variant={
|
|
83
|
+
status === "success"
|
|
84
|
+
? "default"
|
|
85
|
+
: status === "error"
|
|
86
|
+
? "destructive"
|
|
87
|
+
: "secondary"
|
|
88
|
+
}
|
|
89
|
+
className="text-xs"
|
|
90
|
+
>
|
|
91
|
+
{label}
|
|
92
|
+
</Badge>
|
|
93
|
+
</div>
|
|
94
|
+
</TableCell>
|
|
95
|
+
<TableCell>
|
|
96
|
+
<code className="text-sm bg-gray-100 px-2 py-1 rounded">
|
|
97
|
+
{build.package_build_id.slice(-8)}
|
|
98
|
+
</code>
|
|
99
|
+
</TableCell>
|
|
100
|
+
<TableCell>
|
|
101
|
+
<div className="flex items-center gap-2">
|
|
102
|
+
{build.branch_name?.includes("/") ? (
|
|
103
|
+
<GitBranch className="w-3 h-3 text-gray-500" />
|
|
104
|
+
) : (
|
|
105
|
+
<GitCommit className="w-3 h-3 text-gray-500" />
|
|
106
|
+
)}
|
|
107
|
+
<Badge variant="outline" className="text-xs">
|
|
108
|
+
{build.branch_name || "main"}
|
|
109
|
+
</Badge>
|
|
110
|
+
</div>
|
|
111
|
+
</TableCell>
|
|
112
|
+
<TableCell>
|
|
113
|
+
<div className="max-w-xs">
|
|
114
|
+
<p className="text-sm font-medium truncate">
|
|
115
|
+
{build.commit_message || "No commit message"}
|
|
116
|
+
</p>
|
|
117
|
+
</div>
|
|
118
|
+
</TableCell>
|
|
119
|
+
<TableCell>
|
|
120
|
+
<span className="text-sm text-gray-600">
|
|
121
|
+
{build.commit_author || "Unknown"}
|
|
122
|
+
</span>
|
|
123
|
+
</TableCell>
|
|
124
|
+
<TableCell>
|
|
125
|
+
<span className="text-sm text-gray-600">
|
|
126
|
+
{formatTimeAgo(build.created_at)}
|
|
127
|
+
</span>
|
|
128
|
+
</TableCell>
|
|
129
|
+
<TableCell>
|
|
130
|
+
<div className="flex items-center gap-2">
|
|
131
|
+
<DropdownMenu>
|
|
132
|
+
<DropdownMenuTrigger asChild>
|
|
133
|
+
<Button
|
|
134
|
+
variant="ghost"
|
|
135
|
+
size="sm"
|
|
136
|
+
onClick={(e) => e.stopPropagation()}
|
|
137
|
+
>
|
|
138
|
+
<MoreHorizontal className="w-3 h-3" />
|
|
139
|
+
</Button>
|
|
140
|
+
</DropdownMenuTrigger>
|
|
141
|
+
<DropdownMenuContent align="end">
|
|
142
|
+
<DropdownMenuItem>View Details</DropdownMenuItem>
|
|
143
|
+
<DropdownMenuItem>View Logs</DropdownMenuItem>
|
|
144
|
+
<DropdownMenuItem>Redeploy</DropdownMenuItem>
|
|
145
|
+
<DropdownMenuItem className="text-red-600">
|
|
146
|
+
Cancel
|
|
147
|
+
</DropdownMenuItem>
|
|
148
|
+
</DropdownMenuContent>
|
|
149
|
+
</DropdownMenu>
|
|
150
|
+
</div>
|
|
151
|
+
</TableCell>
|
|
152
|
+
</TableRow>
|
|
153
|
+
)
|
|
154
|
+
})}
|
|
155
|
+
</TableBody>
|
|
156
|
+
</Table>
|
|
157
|
+
</div>
|
|
158
|
+
</CardContent>
|
|
159
|
+
</Card>
|
|
160
|
+
|
|
161
|
+
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
162
|
+
<Card>
|
|
163
|
+
<CardContent className="p-6">
|
|
164
|
+
<div className="flex items-center gap-3">
|
|
165
|
+
<div className="p-2 bg-green-100 rounded-lg">
|
|
166
|
+
<CheckCircle className="w-5 h-5 text-green-600" />
|
|
167
|
+
</div>
|
|
168
|
+
<div>
|
|
169
|
+
<p className="text-sm text-gray-600">Successful</p>
|
|
170
|
+
<p className="text-2xl font-bold text-gray-900">
|
|
171
|
+
{
|
|
172
|
+
builds.filter((d) => getBuildStatus(d).status === "success")
|
|
173
|
+
.length
|
|
174
|
+
}
|
|
175
|
+
</p>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</CardContent>
|
|
179
|
+
</Card>
|
|
180
|
+
|
|
181
|
+
<Card>
|
|
182
|
+
<CardContent className="p-6">
|
|
183
|
+
<div className="flex items-center gap-3">
|
|
184
|
+
<div className="p-2 bg-red-100 rounded-lg">
|
|
185
|
+
<AlertCircle className="w-5 h-5 text-red-600" />
|
|
186
|
+
</div>
|
|
187
|
+
<div>
|
|
188
|
+
<p className="text-sm text-gray-600">Failed</p>
|
|
189
|
+
<p className="text-2xl font-bold text-gray-900">
|
|
190
|
+
{
|
|
191
|
+
builds.filter((d) => getBuildStatus(d).status === "error")
|
|
192
|
+
.length
|
|
193
|
+
}
|
|
194
|
+
</p>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</CardContent>
|
|
198
|
+
</Card>
|
|
199
|
+
|
|
200
|
+
<Card>
|
|
201
|
+
<CardContent className="p-6">
|
|
202
|
+
<div className="flex items-center gap-3">
|
|
203
|
+
<div className="p-2 bg-blue-100 rounded-lg">
|
|
204
|
+
<Loader2 className="w-5 h-5 text-blue-600" />
|
|
205
|
+
</div>
|
|
206
|
+
<div>
|
|
207
|
+
<p className="text-sm text-gray-600">Building</p>
|
|
208
|
+
<p className="text-2xl font-bold text-gray-900">
|
|
209
|
+
{
|
|
210
|
+
builds.filter(
|
|
211
|
+
(d) => getBuildStatus(d).status === "building",
|
|
212
|
+
).length
|
|
213
|
+
}
|
|
214
|
+
</p>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
</CardContent>
|
|
218
|
+
</Card>
|
|
219
|
+
|
|
220
|
+
<Card>
|
|
221
|
+
<CardContent className="p-6">
|
|
222
|
+
<div className="flex items-center gap-3">
|
|
223
|
+
<div className="p-2 bg-gray-100 rounded-lg">
|
|
224
|
+
<Clock className="w-5 h-5 text-gray-600" />
|
|
225
|
+
</div>
|
|
226
|
+
<div>
|
|
227
|
+
<p className="text-sm text-gray-600">Total</p>
|
|
228
|
+
<p className="text-2xl font-bold text-gray-900">
|
|
229
|
+
{builds.length}
|
|
230
|
+
</p>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</CardContent>
|
|
234
|
+
</Card>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
)
|
|
238
|
+
}
|