@wakastellar/ui 2.1.2 → 2.3.2

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.
Files changed (123) hide show
  1. package/dist/blocks/apm-overview/index.d.ts +58 -0
  2. package/dist/blocks/cicd-builder/index.d.ts +47 -0
  3. package/dist/blocks/cloud-cost-dashboard/index.d.ts +49 -0
  4. package/dist/blocks/container-orchestrator/index.d.ts +63 -0
  5. package/dist/blocks/database-admin/index.d.ts +84 -0
  6. package/dist/blocks/gitops-sync-status/index.d.ts +45 -0
  7. package/dist/blocks/incident-manager/index.d.ts +44 -0
  8. package/dist/blocks/index.d.ts +10 -0
  9. package/dist/blocks/infrastructure-map/index.d.ts +32 -0
  10. package/dist/blocks/on-call-schedule/index.d.ts +43 -0
  11. package/dist/blocks/release-notes/index.d.ts +49 -0
  12. package/dist/components/index.d.ts +34 -0
  13. package/dist/components/waka-ad-banner/index.d.ts +36 -0
  14. package/dist/components/waka-ad-fallback/index.d.ts +33 -0
  15. package/dist/components/waka-ad-inline/index.d.ts +15 -0
  16. package/dist/components/waka-ad-interstitial/index.d.ts +26 -0
  17. package/dist/components/waka-ad-placeholder/index.d.ts +17 -0
  18. package/dist/components/waka-ad-provider/index.d.ts +103 -0
  19. package/dist/components/waka-ad-sidebar/index.d.ts +18 -0
  20. package/dist/components/waka-ad-sticky-footer/index.d.ts +17 -0
  21. package/dist/components/waka-alert-panel/index.d.ts +45 -0
  22. package/dist/components/waka-artifact-list/index.d.ts +32 -0
  23. package/dist/components/waka-build-matrix/index.d.ts +36 -0
  24. package/dist/components/waka-config-comparator/index.d.ts +37 -0
  25. package/dist/components/waka-container-list/index.d.ts +51 -0
  26. package/dist/components/waka-content-recommendation/index.d.ts +23 -0
  27. package/dist/components/waka-database-card/index.d.ts +46 -0
  28. package/dist/components/waka-dependency-tree/index.d.ts +38 -0
  29. package/dist/components/waka-env-var-editor/index.d.ts +30 -0
  30. package/dist/components/waka-feature-flag-row/index.d.ts +45 -0
  31. package/dist/components/waka-kubernetes-overview/index.d.ts +98 -0
  32. package/dist/components/waka-log-viewer/index.d.ts +38 -0
  33. package/dist/components/waka-migration-list/index.d.ts +36 -0
  34. package/dist/components/waka-outstream-video/index.d.ts +24 -0
  35. package/dist/components/waka-pod-card/index.d.ts +73 -0
  36. package/dist/components/waka-query-explain/index.d.ts +48 -0
  37. package/dist/components/waka-secret-card/index.d.ts +43 -0
  38. package/dist/components/waka-security-scan-result/index.d.ts +45 -0
  39. package/dist/components/waka-service-graph/index.d.ts +44 -0
  40. package/dist/components/waka-sponsored-badge/index.d.ts +20 -0
  41. package/dist/components/waka-sponsored-card/index.d.ts +25 -0
  42. package/dist/components/waka-sponsored-feed/index.d.ts +31 -0
  43. package/dist/components/waka-test-report/index.d.ts +60 -0
  44. package/dist/components/waka-trace-viewer/index.d.ts +36 -0
  45. package/dist/components/waka-video-ad/index.d.ts +32 -0
  46. package/dist/components/waka-video-overlay/index.d.ts +26 -0
  47. package/dist/index.cjs.js +251 -200
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.es.js +47315 -35823
  50. package/dist/utils/security.d.ts +96 -0
  51. package/package.json +4 -4
  52. package/src/blocks/apm-overview/index.tsx +672 -0
  53. package/src/blocks/cicd-builder/index.tsx +738 -0
  54. package/src/blocks/cloud-cost-dashboard/index.tsx +597 -0
  55. package/src/blocks/container-orchestrator/index.tsx +729 -0
  56. package/src/blocks/database-admin/index.tsx +679 -0
  57. package/src/blocks/gitops-sync-status/index.tsx +557 -0
  58. package/src/blocks/incident-manager/index.tsx +586 -0
  59. package/src/blocks/index.ts +119 -0
  60. package/src/blocks/infrastructure-map/index.tsx +638 -0
  61. package/src/blocks/on-call-schedule/index.tsx +615 -0
  62. package/src/blocks/release-notes/index.tsx +643 -0
  63. package/src/blocks/sidebar/index.tsx +6 -6
  64. package/src/components/DataTable/templates/index.tsx +3 -2
  65. package/src/components/index.ts +283 -0
  66. package/src/components/waka-3d-pie-chart/index.tsx +11 -11
  67. package/src/components/waka-achievement-unlock/index.tsx +16 -16
  68. package/src/components/waka-ad-banner/index.tsx +275 -0
  69. package/src/components/waka-ad-fallback/index.tsx +181 -0
  70. package/src/components/waka-ad-inline/index.tsx +103 -0
  71. package/src/components/waka-ad-interstitial/index.tsx +278 -0
  72. package/src/components/waka-ad-placeholder/index.tsx +84 -0
  73. package/src/components/waka-ad-provider/index.tsx +329 -0
  74. package/src/components/waka-ad-sidebar/index.tsx +113 -0
  75. package/src/components/waka-ad-sticky-footer/index.tsx +125 -0
  76. package/src/components/waka-alert-panel/index.tsx +493 -0
  77. package/src/components/waka-artifact-list/index.tsx +416 -0
  78. package/src/components/waka-badge-showcase/index.tsx +12 -11
  79. package/src/components/waka-build-matrix/index.tsx +396 -0
  80. package/src/components/waka-command-bar/index.tsx +2 -1
  81. package/src/components/waka-config-comparator/index.tsx +416 -0
  82. package/src/components/waka-container-list/index.tsx +475 -0
  83. package/src/components/waka-content-recommendation/index.tsx +294 -0
  84. package/src/components/waka-cost-breakdown/index.tsx +10 -10
  85. package/src/components/waka-database-card/index.tsx +473 -0
  86. package/src/components/waka-dependency-tree/index.tsx +542 -0
  87. package/src/components/waka-env-var-editor/index.tsx +417 -0
  88. package/src/components/waka-feature-flag-row/index.tsx +386 -0
  89. package/src/components/waka-funnel-chart/index.tsx +8 -8
  90. package/src/components/waka-health-pulse/index.tsx +6 -6
  91. package/src/components/waka-kubernetes-overview/index.tsx +536 -0
  92. package/src/components/waka-leaderboard/index.tsx +9 -9
  93. package/src/components/waka-log-viewer/index.tsx +386 -0
  94. package/src/components/waka-loot-box/index.tsx +20 -20
  95. package/src/components/waka-migration-list/index.tsx +487 -0
  96. package/src/components/waka-outstream-video/index.tsx +240 -0
  97. package/src/components/waka-player-card/index.tsx +5 -5
  98. package/src/components/waka-pod-card/index.tsx +528 -0
  99. package/src/components/waka-query-explain/index.tsx +657 -0
  100. package/src/components/waka-quota-bar/index.tsx +4 -4
  101. package/src/components/waka-radar-score/index.tsx +10 -10
  102. package/src/components/waka-scratch-card/index.tsx +5 -4
  103. package/src/components/waka-secret-card/index.tsx +371 -0
  104. package/src/components/waka-security-scan-result/index.tsx +473 -0
  105. package/src/components/waka-server-rack/index.tsx +28 -27
  106. package/src/components/waka-service-graph/index.tsx +445 -0
  107. package/src/components/waka-sponsored-badge/index.tsx +97 -0
  108. package/src/components/waka-sponsored-card/index.tsx +275 -0
  109. package/src/components/waka-sponsored-feed/index.tsx +127 -0
  110. package/src/components/waka-spotlight/index.tsx +2 -1
  111. package/src/components/waka-success-explosion/index.tsx +4 -4
  112. package/src/components/waka-test-report/index.tsx +469 -0
  113. package/src/components/waka-trace-viewer/index.tsx +490 -0
  114. package/src/components/waka-video-ad/index.tsx +406 -0
  115. package/src/components/waka-video-overlay/index.tsx +257 -0
  116. package/src/components/waka-xp-bar/index.tsx +13 -13
  117. package/src/styles/base.css +16 -0
  118. package/src/styles/tailwind.preset.js +12 -0
  119. package/src/styles/themes/forest.css +16 -0
  120. package/src/styles/themes/monochrome.css +16 -0
  121. package/src/styles/themes/perpetuity.css +16 -0
  122. package/src/styles/themes/sunset.css +16 -0
  123. package/src/styles/themes/twilight.css +16 -0
@@ -0,0 +1,542 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../utils/cn"
5
+ import { Badge } from "../badge"
6
+ import { Button } from "../button"
7
+ import { Input } from "../input"
8
+ import { ScrollArea } from "../scroll-area"
9
+ import {
10
+ Tooltip,
11
+ TooltipContent,
12
+ TooltipProvider,
13
+ TooltipTrigger,
14
+ } from "../tooltip"
15
+ import {
16
+ Package,
17
+ ChevronDown,
18
+ ChevronRight,
19
+ Search,
20
+ AlertTriangle,
21
+ AlertCircle,
22
+ Shield,
23
+ ShieldAlert,
24
+ ExternalLink,
25
+ GitBranch,
26
+ ArrowUpCircle,
27
+ } from "lucide-react"
28
+
29
+ export type DependencyType = "direct" | "transitive" | "dev" | "peer" | "optional"
30
+ export type VulnSeverity = "critical" | "high" | "medium" | "low" | "none"
31
+
32
+ export interface Dependency {
33
+ id: string
34
+ name: string
35
+ version: string
36
+ latestVersion?: string
37
+ type: DependencyType
38
+ license?: string
39
+ vulnerabilities?: {
40
+ critical: number
41
+ high: number
42
+ medium: number
43
+ low: number
44
+ }
45
+ outdated?: boolean
46
+ children?: Dependency[]
47
+ size?: number // in bytes
48
+ repository?: string
49
+ }
50
+
51
+ export interface WakaDependencyTreeProps {
52
+ /** Root dependencies */
53
+ dependencies: Dependency[]
54
+ /** Callback when clicking on a dependency */
55
+ onDependencyClick?: (dep: Dependency) => void
56
+ /** Callback when upgrading a dependency */
57
+ onUpgrade?: (dep: Dependency) => void
58
+ /** Title */
59
+ title?: string
60
+ /** Show dev dependencies */
61
+ showDevDeps?: boolean
62
+ /** Show only vulnerable */
63
+ showVulnerableOnly?: boolean
64
+ /** Custom class name */
65
+ className?: string
66
+ }
67
+
68
+ const typeConfig: Record<DependencyType, { label: string; color: string }> = {
69
+ direct: { label: "Direct", color: "bg-blue-500" },
70
+ transitive: { label: "Transitive", color: "bg-gray-500" },
71
+ dev: { label: "Dev", color: "bg-purple-500" },
72
+ peer: { label: "Peer", color: "bg-orange-500" },
73
+ optional: { label: "Optional", color: "bg-cyan-500" },
74
+ }
75
+
76
+ const vulnColors: Record<VulnSeverity, string> = {
77
+ critical: "text-red-600",
78
+ high: "text-orange-500",
79
+ medium: "text-yellow-500",
80
+ low: "text-blue-500",
81
+ none: "text-green-500",
82
+ }
83
+
84
+ function getHighestSeverity(vuln?: Dependency["vulnerabilities"]): VulnSeverity {
85
+ if (!vuln) return "none"
86
+ if (vuln.critical > 0) return "critical"
87
+ if (vuln.high > 0) return "high"
88
+ if (vuln.medium > 0) return "medium"
89
+ if (vuln.low > 0) return "low"
90
+ return "none"
91
+ }
92
+
93
+ function getTotalVulns(vuln?: Dependency["vulnerabilities"]): number {
94
+ if (!vuln) return 0
95
+ return vuln.critical + vuln.high + vuln.medium + vuln.low
96
+ }
97
+
98
+ function formatBytes(bytes?: number): string {
99
+ if (!bytes) return ""
100
+ if (bytes < 1024) return `${bytes} B`
101
+ const k = 1024
102
+ const sizes = ["B", "KB", "MB"]
103
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
104
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i]
105
+ }
106
+
107
+ function DependencyNode({
108
+ dependency,
109
+ depth,
110
+ isExpanded,
111
+ onToggle,
112
+ onDependencyClick,
113
+ onUpgrade,
114
+ }: {
115
+ dependency: Dependency
116
+ depth: number
117
+ isExpanded: boolean
118
+ onToggle: () => void
119
+ onDependencyClick?: (dep: Dependency) => void
120
+ onUpgrade?: (dep: Dependency) => void
121
+ }) {
122
+ const hasChildren = dependency.children && dependency.children.length > 0
123
+ const severity = getHighestSeverity(dependency.vulnerabilities)
124
+ const totalVulns = getTotalVulns(dependency.vulnerabilities)
125
+ const typeConf = typeConfig[dependency.type]
126
+
127
+ return (
128
+ <div
129
+ className={cn(
130
+ "flex items-center gap-2 px-2 py-1.5 hover:bg-muted/50 transition-colors rounded",
131
+ severity === "critical" && "bg-red-500/5",
132
+ severity === "high" && "bg-orange-500/5"
133
+ )}
134
+ style={{ paddingLeft: `${depth * 20 + 8}px` }}
135
+ >
136
+ {/* Expand button */}
137
+ <button
138
+ className="w-5 h-5 flex items-center justify-center text-muted-foreground"
139
+ onClick={onToggle}
140
+ disabled={!hasChildren}
141
+ >
142
+ {hasChildren ? (
143
+ isExpanded ? (
144
+ <ChevronDown className="h-4 w-4" />
145
+ ) : (
146
+ <ChevronRight className="h-4 w-4" />
147
+ )
148
+ ) : (
149
+ <span className="w-4" />
150
+ )}
151
+ </button>
152
+
153
+ {/* Package icon with severity indicator */}
154
+ <div className="relative">
155
+ <Package className="h-4 w-4 text-muted-foreground" />
156
+ {severity !== "none" && (
157
+ <div className={cn("absolute -top-1 -right-1 w-2 h-2 rounded-full",
158
+ severity === "critical" ? "bg-red-500" :
159
+ severity === "high" ? "bg-orange-500" :
160
+ severity === "medium" ? "bg-yellow-500" : "bg-blue-500"
161
+ )} />
162
+ )}
163
+ </div>
164
+
165
+ {/* Package name */}
166
+ <button
167
+ className="font-medium text-sm hover:text-primary hover:underline"
168
+ onClick={() => onDependencyClick?.(dependency)}
169
+ >
170
+ {dependency.name}
171
+ </button>
172
+
173
+ {/* Version */}
174
+ <code className="text-xs bg-muted px-1.5 py-0.5 rounded">
175
+ {dependency.version}
176
+ </code>
177
+
178
+ {/* Type badge */}
179
+ <Badge variant="outline" className="text-xs">
180
+ {typeConf.label}
181
+ </Badge>
182
+
183
+ {/* Vulnerabilities */}
184
+ {totalVulns > 0 && (
185
+ <TooltipProvider>
186
+ <Tooltip>
187
+ <TooltipTrigger asChild>
188
+ <Badge className={cn("text-xs text-white",
189
+ severity === "critical" ? "bg-red-500" :
190
+ severity === "high" ? "bg-orange-500" :
191
+ severity === "medium" ? "bg-yellow-500" : "bg-blue-500"
192
+ )}>
193
+ <ShieldAlert className="h-3 w-3 mr-1" />
194
+ {totalVulns}
195
+ </Badge>
196
+ </TooltipTrigger>
197
+ <TooltipContent>
198
+ <div className="space-y-1 text-xs">
199
+ {dependency.vulnerabilities?.critical! > 0 && (
200
+ <div className="text-red-500">{dependency.vulnerabilities?.critical} critical</div>
201
+ )}
202
+ {dependency.vulnerabilities?.high! > 0 && (
203
+ <div className="text-orange-500">{dependency.vulnerabilities?.high} high</div>
204
+ )}
205
+ {dependency.vulnerabilities?.medium! > 0 && (
206
+ <div className="text-yellow-500">{dependency.vulnerabilities?.medium} medium</div>
207
+ )}
208
+ {dependency.vulnerabilities?.low! > 0 && (
209
+ <div className="text-blue-500">{dependency.vulnerabilities?.low} low</div>
210
+ )}
211
+ </div>
212
+ </TooltipContent>
213
+ </Tooltip>
214
+ </TooltipProvider>
215
+ )}
216
+
217
+ {/* Outdated indicator */}
218
+ {dependency.outdated && dependency.latestVersion && (
219
+ <TooltipProvider>
220
+ <Tooltip>
221
+ <TooltipTrigger asChild>
222
+ <Button
223
+ variant="ghost"
224
+ size="sm"
225
+ className="h-6 px-2 text-xs text-yellow-500"
226
+ onClick={(e) => {
227
+ e.stopPropagation()
228
+ onUpgrade?.(dependency)
229
+ }}
230
+ >
231
+ <ArrowUpCircle className="h-3 w-3 mr-1" />
232
+ {dependency.latestVersion}
233
+ </Button>
234
+ </TooltipTrigger>
235
+ <TooltipContent>Upgrade to {dependency.latestVersion}</TooltipContent>
236
+ </Tooltip>
237
+ </TooltipProvider>
238
+ )}
239
+
240
+ {/* License */}
241
+ {dependency.license && (
242
+ <span className="text-xs text-muted-foreground ml-auto">
243
+ {dependency.license}
244
+ </span>
245
+ )}
246
+
247
+ {/* Size */}
248
+ {dependency.size && (
249
+ <span className="text-xs text-muted-foreground">
250
+ {formatBytes(dependency.size)}
251
+ </span>
252
+ )}
253
+ </div>
254
+ )
255
+ }
256
+
257
+ function DependencyTree({
258
+ dependencies,
259
+ depth,
260
+ expandedIds,
261
+ onToggle,
262
+ onDependencyClick,
263
+ onUpgrade,
264
+ searchQuery,
265
+ }: {
266
+ dependencies: Dependency[]
267
+ depth: number
268
+ expandedIds: Set<string>
269
+ onToggle: (id: string) => void
270
+ onDependencyClick?: (dep: Dependency) => void
271
+ onUpgrade?: (dep: Dependency) => void
272
+ searchQuery: string
273
+ }) {
274
+ return (
275
+ <>
276
+ {dependencies.map((dep) => {
277
+ const isExpanded = expandedIds.has(dep.id)
278
+ const matchesSearch = !searchQuery || dep.name.toLowerCase().includes(searchQuery.toLowerCase())
279
+
280
+ if (!matchesSearch && !dep.children?.some(c => c.name.toLowerCase().includes(searchQuery.toLowerCase()))) {
281
+ return null
282
+ }
283
+
284
+ return (
285
+ <React.Fragment key={dep.id}>
286
+ <DependencyNode
287
+ dependency={dep}
288
+ depth={depth}
289
+ isExpanded={isExpanded}
290
+ onToggle={() => onToggle(dep.id)}
291
+ onDependencyClick={onDependencyClick}
292
+ onUpgrade={onUpgrade}
293
+ />
294
+ {isExpanded && dep.children && (
295
+ <DependencyTree
296
+ dependencies={dep.children}
297
+ depth={depth + 1}
298
+ expandedIds={expandedIds}
299
+ onToggle={onToggle}
300
+ onDependencyClick={onDependencyClick}
301
+ onUpgrade={onUpgrade}
302
+ searchQuery={searchQuery}
303
+ />
304
+ )}
305
+ </React.Fragment>
306
+ )
307
+ })}
308
+ </>
309
+ )
310
+ }
311
+
312
+ export function WakaDependencyTree({
313
+ dependencies,
314
+ onDependencyClick,
315
+ onUpgrade,
316
+ title = "Dependencies",
317
+ showDevDeps = true,
318
+ showVulnerableOnly = false,
319
+ className,
320
+ }: WakaDependencyTreeProps) {
321
+ const [searchQuery, setSearchQuery] = React.useState("")
322
+ const [expandedIds, setExpandedIds] = React.useState<Set<string>>(new Set())
323
+ const [filterVulnerable, setFilterVulnerable] = React.useState(showVulnerableOnly)
324
+
325
+ const toggleExpand = (id: string) => {
326
+ setExpandedIds((prev) => {
327
+ const next = new Set(prev)
328
+ if (next.has(id)) {
329
+ next.delete(id)
330
+ } else {
331
+ next.add(id)
332
+ }
333
+ return next
334
+ })
335
+ }
336
+
337
+ const expandAll = () => {
338
+ const allIds = new Set<string>()
339
+ const collectIds = (deps: Dependency[]) => {
340
+ deps.forEach((dep) => {
341
+ allIds.add(dep.id)
342
+ if (dep.children) collectIds(dep.children)
343
+ })
344
+ }
345
+ collectIds(dependencies)
346
+ setExpandedIds(allIds)
347
+ }
348
+
349
+ const collapseAll = () => {
350
+ setExpandedIds(new Set())
351
+ }
352
+
353
+ // Filter dependencies
354
+ const filteredDeps = React.useMemo(() => {
355
+ let filtered = dependencies
356
+ if (!showDevDeps) {
357
+ filtered = filtered.filter((d) => d.type !== "dev")
358
+ }
359
+ if (filterVulnerable) {
360
+ const hasVulns = (dep: Dependency): boolean => {
361
+ if (getTotalVulns(dep.vulnerabilities) > 0) return true
362
+ return dep.children?.some(hasVulns) || false
363
+ }
364
+ filtered = filtered.filter(hasVulns)
365
+ }
366
+ return filtered
367
+ }, [dependencies, showDevDeps, filterVulnerable])
368
+
369
+ // Calculate stats
370
+ const stats = React.useMemo(() => {
371
+ let total = 0
372
+ let vulnerable = 0
373
+ let outdated = 0
374
+ let criticalVulns = 0
375
+
376
+ const count = (deps: Dependency[]) => {
377
+ deps.forEach((dep) => {
378
+ total++
379
+ if (getTotalVulns(dep.vulnerabilities) > 0) vulnerable++
380
+ if (dep.vulnerabilities?.critical) criticalVulns += dep.vulnerabilities.critical
381
+ if (dep.outdated) outdated++
382
+ if (dep.children) count(dep.children)
383
+ })
384
+ }
385
+ count(dependencies)
386
+
387
+ return { total, vulnerable, outdated, criticalVulns }
388
+ }, [dependencies])
389
+
390
+ return (
391
+ <div className={cn("flex flex-col border rounded-lg bg-background", className)}>
392
+ {/* Header */}
393
+ <div className="flex items-center justify-between gap-4 p-3 border-b">
394
+ <div className="flex items-center gap-3">
395
+ <GitBranch className="h-5 w-5" />
396
+ <h3 className="font-semibold">{title}</h3>
397
+ <Badge variant="secondary">{stats.total}</Badge>
398
+ </div>
399
+
400
+ <div className="flex items-center gap-2">
401
+ {stats.criticalVulns > 0 && (
402
+ <Badge className="bg-red-500">{stats.criticalVulns} critical</Badge>
403
+ )}
404
+ {stats.vulnerable > 0 && (
405
+ <Badge variant="secondary" className="text-orange-500">
406
+ {stats.vulnerable} vulnerable
407
+ </Badge>
408
+ )}
409
+ {stats.outdated > 0 && (
410
+ <Badge variant="outline">{stats.outdated} outdated</Badge>
411
+ )}
412
+ </div>
413
+ </div>
414
+
415
+ {/* Toolbar */}
416
+ <div className="flex items-center gap-2 p-2 border-b bg-muted/30">
417
+ <div className="relative flex-1 max-w-sm">
418
+ <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
419
+ <Input
420
+ placeholder="Search packages..."
421
+ value={searchQuery}
422
+ onChange={(e) => setSearchQuery(e.target.value)}
423
+ className="pl-8 h-8"
424
+ />
425
+ </div>
426
+
427
+ <Button
428
+ variant={filterVulnerable ? "default" : "outline"}
429
+ size="sm"
430
+ className="h-8"
431
+ onClick={() => setFilterVulnerable(!filterVulnerable)}
432
+ >
433
+ <ShieldAlert className="h-4 w-4 mr-1" />
434
+ Vulnerable only
435
+ </Button>
436
+
437
+ <div className="flex items-center gap-1">
438
+ <Button variant="ghost" size="sm" className="h-8" onClick={expandAll}>
439
+ Expand All
440
+ </Button>
441
+ <Button variant="ghost" size="sm" className="h-8" onClick={collapseAll}>
442
+ Collapse All
443
+ </Button>
444
+ </div>
445
+ </div>
446
+
447
+ {/* Tree */}
448
+ <ScrollArea className="flex-1 max-h-[500px]">
449
+ <div className="py-2">
450
+ {filteredDeps.length === 0 ? (
451
+ <div className="flex flex-col items-center justify-center h-32 text-muted-foreground">
452
+ <Package className="h-8 w-8 mb-2" />
453
+ <span>No dependencies found</span>
454
+ </div>
455
+ ) : (
456
+ <DependencyTree
457
+ dependencies={filteredDeps}
458
+ depth={0}
459
+ expandedIds={expandedIds}
460
+ onToggle={toggleExpand}
461
+ onDependencyClick={onDependencyClick}
462
+ onUpgrade={onUpgrade}
463
+ searchQuery={searchQuery}
464
+ />
465
+ )}
466
+ </div>
467
+ </ScrollArea>
468
+ </div>
469
+ )
470
+ }
471
+
472
+ // Default sample dependencies for demo
473
+ export const defaultDependencies: Dependency[] = [
474
+ {
475
+ id: "1",
476
+ name: "react",
477
+ version: "18.2.0",
478
+ latestVersion: "18.2.0",
479
+ type: "direct",
480
+ license: "MIT",
481
+ size: 2.5 * 1024 * 1024,
482
+ children: [
483
+ {
484
+ id: "1-1",
485
+ name: "loose-envify",
486
+ version: "1.4.0",
487
+ type: "transitive",
488
+ license: "MIT",
489
+ },
490
+ ],
491
+ },
492
+ {
493
+ id: "2",
494
+ name: "lodash",
495
+ version: "4.17.19",
496
+ latestVersion: "4.17.21",
497
+ type: "direct",
498
+ license: "MIT",
499
+ outdated: true,
500
+ vulnerabilities: { critical: 0, high: 1, medium: 1, low: 0 },
501
+ size: 530 * 1024,
502
+ },
503
+ {
504
+ id: "3",
505
+ name: "axios",
506
+ version: "0.21.1",
507
+ latestVersion: "1.6.0",
508
+ type: "direct",
509
+ license: "MIT",
510
+ outdated: true,
511
+ vulnerabilities: { critical: 1, high: 0, medium: 0, low: 0 },
512
+ size: 450 * 1024,
513
+ children: [
514
+ {
515
+ id: "3-1",
516
+ name: "follow-redirects",
517
+ version: "1.14.0",
518
+ type: "transitive",
519
+ license: "MIT",
520
+ vulnerabilities: { critical: 0, high: 1, medium: 0, low: 0 },
521
+ },
522
+ ],
523
+ },
524
+ {
525
+ id: "4",
526
+ name: "typescript",
527
+ version: "5.0.0",
528
+ latestVersion: "5.3.0",
529
+ type: "dev",
530
+ license: "Apache-2.0",
531
+ outdated: true,
532
+ size: 15 * 1024 * 1024,
533
+ },
534
+ {
535
+ id: "5",
536
+ name: "eslint",
537
+ version: "8.50.0",
538
+ type: "dev",
539
+ license: "MIT",
540
+ size: 3 * 1024 * 1024,
541
+ },
542
+ ]