@wakastellar/ui 2.1.1 → 2.1.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.
@@ -0,0 +1,609 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../utils"
5
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../components/card"
6
+ import { Button } from "../../components/button"
7
+ import { Badge } from "../../components/badge"
8
+ import { Progress } from "../../components/progress"
9
+ import { Avatar, AvatarFallback, AvatarImage } from "../../components/avatar"
10
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../components/tabs"
11
+ import { Separator } from "../../components/separator"
12
+ import {
13
+ Select,
14
+ SelectContent,
15
+ SelectItem,
16
+ SelectTrigger,
17
+ SelectValue,
18
+ } from "../../components/select"
19
+ import {
20
+ Rocket,
21
+ GitBranch,
22
+ GitCommit,
23
+ GitMerge,
24
+ Clock,
25
+ CheckCircle,
26
+ XCircle,
27
+ AlertCircle,
28
+ Loader2,
29
+ Play,
30
+ Pause,
31
+ RotateCcw,
32
+ ExternalLink,
33
+ Terminal,
34
+ Server,
35
+ Globe,
36
+ Shield,
37
+ Activity,
38
+ ChevronRight,
39
+ RefreshCw,
40
+ Settings,
41
+ MoreVertical,
42
+ Zap,
43
+ Cloud,
44
+ Database,
45
+ } from "lucide-react"
46
+
47
+ // ============================================
48
+ // TYPES
49
+ // ============================================
50
+
51
+ export type DeploymentStatus = "pending" | "building" | "deploying" | "success" | "failed" | "cancelled" | "rollback"
52
+ export type Environment = "development" | "staging" | "production"
53
+
54
+ export interface Deployment {
55
+ id: string
56
+ status: DeploymentStatus
57
+ environment: Environment
58
+ branch: string
59
+ commit: string
60
+ commitMessage: string
61
+ author: {
62
+ name: string
63
+ avatar?: string
64
+ }
65
+ startedAt: Date | string
66
+ finishedAt?: Date | string
67
+ duration?: string
68
+ url?: string
69
+ logs?: string[]
70
+ }
71
+
72
+ export interface Pipeline {
73
+ id: string
74
+ name: string
75
+ status: DeploymentStatus
76
+ stages: PipelineStage[]
77
+ trigger: string
78
+ branch: string
79
+ startedAt: Date | string
80
+ }
81
+
82
+ export interface PipelineStage {
83
+ id: string
84
+ name: string
85
+ status: DeploymentStatus
86
+ duration?: string
87
+ logs?: string[]
88
+ }
89
+
90
+ export interface EnvironmentStatus {
91
+ name: Environment
92
+ status: "healthy" | "degraded" | "down"
93
+ url: string
94
+ lastDeployment?: Deployment
95
+ uptime?: string
96
+ responseTime?: string
97
+ }
98
+
99
+ export interface DeploymentDashboardProps {
100
+ /** Recent deployments */
101
+ deployments?: Deployment[]
102
+ /** Active pipelines */
103
+ pipelines?: Pipeline[]
104
+ /** Environment statuses */
105
+ environments?: EnvironmentStatus[]
106
+ /** Handler for deploying */
107
+ onDeploy?: (environment: Environment, branch: string) => void
108
+ /** Handler for rollback */
109
+ onRollback?: (deploymentId: string) => void
110
+ /** Handler for cancelling */
111
+ onCancel?: (deploymentId: string) => void
112
+ /** Handler for retrying */
113
+ onRetry?: (deploymentId: string) => void
114
+ /** Available branches */
115
+ branches?: string[]
116
+ /** Custom className */
117
+ className?: string
118
+ }
119
+
120
+ // ============================================
121
+ // SUBCOMPONENTS
122
+ // ============================================
123
+
124
+ const statusConfig: Record<DeploymentStatus, { color: string; icon: React.ReactNode; label: string }> = {
125
+ pending: { color: "text-yellow-500 bg-yellow-500/10", icon: <Clock className="h-4 w-4" />, label: "Pending" },
126
+ building: { color: "text-blue-500 bg-blue-500/10", icon: <Loader2 className="h-4 w-4 animate-spin" />, label: "Building" },
127
+ deploying: { color: "text-purple-500 bg-purple-500/10", icon: <Rocket className="h-4 w-4 animate-pulse" />, label: "Deploying" },
128
+ success: { color: "text-green-500 bg-green-500/10", icon: <CheckCircle className="h-4 w-4" />, label: "Success" },
129
+ failed: { color: "text-red-500 bg-red-500/10", icon: <XCircle className="h-4 w-4" />, label: "Failed" },
130
+ cancelled: { color: "text-gray-500 bg-gray-500/10", icon: <XCircle className="h-4 w-4" />, label: "Cancelled" },
131
+ rollback: { color: "text-orange-500 bg-orange-500/10", icon: <RotateCcw className="h-4 w-4" />, label: "Rollback" },
132
+ }
133
+
134
+ const environmentConfig: Record<Environment, { color: string; icon: React.ReactNode }> = {
135
+ development: { color: "text-blue-500 border-blue-500/30 bg-blue-500/10", icon: <Terminal className="h-4 w-4" /> },
136
+ staging: { color: "text-yellow-500 border-yellow-500/30 bg-yellow-500/10", icon: <Server className="h-4 w-4" /> },
137
+ production: { color: "text-green-500 border-green-500/30 bg-green-500/10", icon: <Globe className="h-4 w-4" /> },
138
+ }
139
+
140
+ function StatusBadge({ status }: { status: DeploymentStatus }) {
141
+ const config = statusConfig[status]
142
+ return (
143
+ <Badge variant="outline" className={cn("gap-1", config.color)}>
144
+ {config.icon}
145
+ {config.label}
146
+ </Badge>
147
+ )
148
+ }
149
+
150
+ function EnvironmentBadge({ environment }: { environment: Environment }) {
151
+ const config = environmentConfig[environment]
152
+ return (
153
+ <Badge variant="outline" className={cn("gap-1 capitalize", config.color)}>
154
+ {config.icon}
155
+ {environment}
156
+ </Badge>
157
+ )
158
+ }
159
+
160
+ function DeploymentCard({
161
+ deployment,
162
+ onRollback,
163
+ onCancel,
164
+ onRetry,
165
+ }: {
166
+ deployment: Deployment
167
+ onRollback?: () => void
168
+ onCancel?: () => void
169
+ onRetry?: () => void
170
+ }) {
171
+ const isInProgress = deployment.status === "building" || deployment.status === "deploying"
172
+ const canRollback = deployment.status === "success" && deployment.environment === "production"
173
+ const canRetry = deployment.status === "failed"
174
+ const canCancel = isInProgress
175
+
176
+ return (
177
+ <div className="p-4 rounded-lg border bg-card hover:bg-muted/50 transition-colors">
178
+ <div className="flex items-start justify-between gap-4">
179
+ <div className="flex items-start gap-3">
180
+ <Avatar className="h-10 w-10">
181
+ <AvatarImage src={deployment.author.avatar} />
182
+ <AvatarFallback>{deployment.author.name.slice(0, 2).toUpperCase()}</AvatarFallback>
183
+ </Avatar>
184
+ <div>
185
+ <div className="flex items-center gap-2 flex-wrap">
186
+ <StatusBadge status={deployment.status} />
187
+ <EnvironmentBadge environment={deployment.environment} />
188
+ </div>
189
+ <p className="text-sm mt-1 line-clamp-1">{deployment.commitMessage}</p>
190
+ <div className="flex items-center gap-3 mt-2 text-xs text-muted-foreground">
191
+ <span className="flex items-center gap-1">
192
+ <GitBranch className="h-3 w-3" />
193
+ {deployment.branch}
194
+ </span>
195
+ <span className="flex items-center gap-1">
196
+ <GitCommit className="h-3 w-3" />
197
+ {deployment.commit.slice(0, 7)}
198
+ </span>
199
+ <span className="flex items-center gap-1">
200
+ <Clock className="h-3 w-3" />
201
+ {typeof deployment.startedAt === "string"
202
+ ? deployment.startedAt
203
+ : formatRelativeTime(deployment.startedAt)}
204
+ </span>
205
+ {deployment.duration && <span>{deployment.duration}</span>}
206
+ </div>
207
+ </div>
208
+ </div>
209
+
210
+ <div className="flex items-center gap-1">
211
+ {deployment.url && (
212
+ <Button variant="ghost" size="icon" asChild>
213
+ <a href={deployment.url} target="_blank" rel="noopener noreferrer">
214
+ <ExternalLink className="h-4 w-4" />
215
+ </a>
216
+ </Button>
217
+ )}
218
+ {canCancel && (
219
+ <Button variant="ghost" size="icon" onClick={onCancel}>
220
+ <Pause className="h-4 w-4" />
221
+ </Button>
222
+ )}
223
+ {canRetry && (
224
+ <Button variant="ghost" size="icon" onClick={onRetry}>
225
+ <RefreshCw className="h-4 w-4" />
226
+ </Button>
227
+ )}
228
+ {canRollback && (
229
+ <Button variant="ghost" size="icon" onClick={onRollback}>
230
+ <RotateCcw className="h-4 w-4" />
231
+ </Button>
232
+ )}
233
+ </div>
234
+ </div>
235
+ </div>
236
+ )
237
+ }
238
+
239
+ function PipelineCard({ pipeline }: { pipeline: Pipeline }) {
240
+ const completedStages = pipeline.stages.filter((s) => s.status === "success").length
241
+ const progress = (completedStages / pipeline.stages.length) * 100
242
+
243
+ return (
244
+ <Card>
245
+ <CardHeader className="pb-2">
246
+ <div className="flex items-center justify-between">
247
+ <div className="flex items-center gap-2">
248
+ <StatusBadge status={pipeline.status} />
249
+ <span className="font-medium">{pipeline.name}</span>
250
+ </div>
251
+ <span className="text-xs text-muted-foreground">
252
+ {typeof pipeline.startedAt === "string"
253
+ ? pipeline.startedAt
254
+ : formatRelativeTime(pipeline.startedAt)}
255
+ </span>
256
+ </div>
257
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
258
+ <GitBranch className="h-3 w-3" />
259
+ {pipeline.branch}
260
+ <span className="mx-1">•</span>
261
+ Triggered by {pipeline.trigger}
262
+ </div>
263
+ </CardHeader>
264
+ <CardContent>
265
+ <div className="space-y-3">
266
+ <Progress value={progress} className="h-2" />
267
+ <div className="flex items-center justify-between">
268
+ {pipeline.stages.map((stage, index) => (
269
+ <React.Fragment key={stage.id}>
270
+ <div className="flex flex-col items-center gap-1">
271
+ <div
272
+ className={cn(
273
+ "h-8 w-8 rounded-full flex items-center justify-center",
274
+ statusConfig[stage.status].color
275
+ )}
276
+ >
277
+ {statusConfig[stage.status].icon}
278
+ </div>
279
+ <span className="text-xs">{stage.name}</span>
280
+ {stage.duration && (
281
+ <span className="text-xs text-muted-foreground">{stage.duration}</span>
282
+ )}
283
+ </div>
284
+ {index < pipeline.stages.length - 1 && (
285
+ <ChevronRight className="h-4 w-4 text-muted-foreground" />
286
+ )}
287
+ </React.Fragment>
288
+ ))}
289
+ </div>
290
+ </div>
291
+ </CardContent>
292
+ </Card>
293
+ )
294
+ }
295
+
296
+ function EnvironmentCard({ env }: { env: EnvironmentStatus }) {
297
+ const healthColors = {
298
+ healthy: "text-green-500",
299
+ degraded: "text-yellow-500",
300
+ down: "text-red-500",
301
+ }
302
+
303
+ const healthIcons = {
304
+ healthy: <CheckCircle className="h-5 w-5" />,
305
+ degraded: <AlertCircle className="h-5 w-5" />,
306
+ down: <XCircle className="h-5 w-5" />,
307
+ }
308
+
309
+ return (
310
+ <Card>
311
+ <CardHeader className="pb-2">
312
+ <div className="flex items-center justify-between">
313
+ <CardTitle className="capitalize flex items-center gap-2">
314
+ {environmentConfig[env.name].icon}
315
+ {env.name}
316
+ </CardTitle>
317
+ <div className={cn("flex items-center gap-1", healthColors[env.status])}>
318
+ {healthIcons[env.status]}
319
+ <span className="text-sm capitalize">{env.status}</span>
320
+ </div>
321
+ </div>
322
+ </CardHeader>
323
+ <CardContent className="space-y-4">
324
+ <div className="flex items-center gap-2 text-sm">
325
+ <ExternalLink className="h-4 w-4 text-muted-foreground" />
326
+ <a
327
+ href={env.url}
328
+ target="_blank"
329
+ rel="noopener noreferrer"
330
+ className="text-primary hover:underline truncate"
331
+ >
332
+ {env.url}
333
+ </a>
334
+ </div>
335
+
336
+ <div className="grid grid-cols-2 gap-4 text-sm">
337
+ {env.uptime && (
338
+ <div>
339
+ <p className="text-muted-foreground">Uptime</p>
340
+ <p className="font-medium">{env.uptime}</p>
341
+ </div>
342
+ )}
343
+ {env.responseTime && (
344
+ <div>
345
+ <p className="text-muted-foreground">Response Time</p>
346
+ <p className="font-medium">{env.responseTime}</p>
347
+ </div>
348
+ )}
349
+ </div>
350
+
351
+ {env.lastDeployment && (
352
+ <div className="pt-2 border-t">
353
+ <p className="text-xs text-muted-foreground mb-1">Last Deployment</p>
354
+ <div className="flex items-center gap-2 text-sm">
355
+ <StatusBadge status={env.lastDeployment.status} />
356
+ <span className="text-muted-foreground">
357
+ {typeof env.lastDeployment.startedAt === "string"
358
+ ? env.lastDeployment.startedAt
359
+ : formatRelativeTime(env.lastDeployment.startedAt)}
360
+ </span>
361
+ </div>
362
+ </div>
363
+ )}
364
+ </CardContent>
365
+ </Card>
366
+ )
367
+ }
368
+
369
+ function formatRelativeTime(date: Date): string {
370
+ const now = new Date()
371
+ const diff = now.getTime() - date.getTime()
372
+ const minutes = Math.floor(diff / 60000)
373
+ const hours = Math.floor(minutes / 60)
374
+ const days = Math.floor(hours / 24)
375
+
376
+ if (minutes < 1) return "just now"
377
+ if (minutes < 60) return `${minutes}m ago`
378
+ if (hours < 24) return `${hours}h ago`
379
+ return `${days}d ago`
380
+ }
381
+
382
+ // ============================================
383
+ // MAIN COMPONENT
384
+ // ============================================
385
+
386
+ export function DeploymentDashboard({
387
+ deployments = [],
388
+ pipelines = [],
389
+ environments = [],
390
+ onDeploy,
391
+ onRollback,
392
+ onCancel,
393
+ onRetry,
394
+ branches = ["main", "develop", "staging"],
395
+ className,
396
+ }: DeploymentDashboardProps) {
397
+ const [selectedBranch, setSelectedBranch] = React.useState(branches[0])
398
+ const [selectedEnvironment, setSelectedEnvironment] = React.useState<Environment>("staging")
399
+
400
+ const activeDeployments = deployments.filter(
401
+ (d) => d.status === "building" || d.status === "deploying"
402
+ )
403
+
404
+ return (
405
+ <div className={cn("space-y-6", className)}>
406
+ {/* Header */}
407
+ <div className="flex flex-col sm:flex-row gap-4 justify-between">
408
+ <div>
409
+ <h1 className="text-2xl font-bold">Deployments</h1>
410
+ <p className="text-muted-foreground">Manage and monitor your deployments</p>
411
+ </div>
412
+ <div className="flex items-center gap-2">
413
+ <Select value={selectedBranch} onValueChange={setSelectedBranch}>
414
+ <SelectTrigger className="w-[160px]">
415
+ <GitBranch className="h-4 w-4 mr-2" />
416
+ <SelectValue />
417
+ </SelectTrigger>
418
+ <SelectContent>
419
+ {branches.map((branch) => (
420
+ <SelectItem key={branch} value={branch}>
421
+ {branch}
422
+ </SelectItem>
423
+ ))}
424
+ </SelectContent>
425
+ </Select>
426
+ <Select value={selectedEnvironment} onValueChange={(v) => setSelectedEnvironment(v as Environment)}>
427
+ <SelectTrigger className="w-[160px]">
428
+ <Server className="h-4 w-4 mr-2" />
429
+ <SelectValue />
430
+ </SelectTrigger>
431
+ <SelectContent>
432
+ <SelectItem value="development">Development</SelectItem>
433
+ <SelectItem value="staging">Staging</SelectItem>
434
+ <SelectItem value="production">Production</SelectItem>
435
+ </SelectContent>
436
+ </Select>
437
+ <Button onClick={() => onDeploy?.(selectedEnvironment, selectedBranch)}>
438
+ <Rocket className="h-4 w-4 mr-2" />
439
+ Deploy
440
+ </Button>
441
+ </div>
442
+ </div>
443
+
444
+ {/* Active Deployments Alert */}
445
+ {activeDeployments.length > 0 && (
446
+ <Card className="border-blue-500/50 bg-blue-500/5">
447
+ <CardContent className="py-4">
448
+ <div className="flex items-center gap-3">
449
+ <div className="h-10 w-10 rounded-full bg-blue-500/10 flex items-center justify-center">
450
+ <Rocket className="h-5 w-5 text-blue-500 animate-pulse" />
451
+ </div>
452
+ <div>
453
+ <p className="font-medium">
454
+ {activeDeployments.length} deployment{activeDeployments.length > 1 ? "s" : ""} in progress
455
+ </p>
456
+ <p className="text-sm text-muted-foreground">
457
+ {activeDeployments.map((d) => d.environment).join(", ")}
458
+ </p>
459
+ </div>
460
+ </div>
461
+ </CardContent>
462
+ </Card>
463
+ )}
464
+
465
+ {/* Environments Grid */}
466
+ <div className="grid gap-4 md:grid-cols-3">
467
+ {environments.map((env) => (
468
+ <EnvironmentCard key={env.name} env={env} />
469
+ ))}
470
+ </div>
471
+
472
+ {/* Tabs */}
473
+ <Tabs defaultValue="deployments" className="w-full">
474
+ <TabsList>
475
+ <TabsTrigger value="deployments">Recent Deployments</TabsTrigger>
476
+ <TabsTrigger value="pipelines">Pipelines</TabsTrigger>
477
+ </TabsList>
478
+
479
+ <TabsContent value="deployments" className="mt-4 space-y-3">
480
+ {deployments.length > 0 ? (
481
+ deployments.map((deployment) => (
482
+ <DeploymentCard
483
+ key={deployment.id}
484
+ deployment={deployment}
485
+ onRollback={() => onRollback?.(deployment.id)}
486
+ onCancel={() => onCancel?.(deployment.id)}
487
+ onRetry={() => onRetry?.(deployment.id)}
488
+ />
489
+ ))
490
+ ) : (
491
+ <Card>
492
+ <CardContent className="py-12 text-center">
493
+ <Rocket className="h-12 w-12 mx-auto text-muted-foreground mb-4" />
494
+ <p className="text-muted-foreground">No deployments yet</p>
495
+ <p className="text-sm text-muted-foreground">
496
+ Deploy your first version to get started
497
+ </p>
498
+ </CardContent>
499
+ </Card>
500
+ )}
501
+ </TabsContent>
502
+
503
+ <TabsContent value="pipelines" className="mt-4 space-y-4">
504
+ {pipelines.length > 0 ? (
505
+ pipelines.map((pipeline) => (
506
+ <PipelineCard key={pipeline.id} pipeline={pipeline} />
507
+ ))
508
+ ) : (
509
+ <Card>
510
+ <CardContent className="py-12 text-center">
511
+ <Activity className="h-12 w-12 mx-auto text-muted-foreground mb-4" />
512
+ <p className="text-muted-foreground">No active pipelines</p>
513
+ <p className="text-sm text-muted-foreground">
514
+ Pipelines will appear here when triggered
515
+ </p>
516
+ </CardContent>
517
+ </Card>
518
+ )}
519
+ </TabsContent>
520
+ </Tabs>
521
+ </div>
522
+ )
523
+ }
524
+
525
+ // ============================================
526
+ // PRESET DATA
527
+ // ============================================
528
+
529
+ export const defaultDeployments: Deployment[] = [
530
+ {
531
+ id: "d1",
532
+ status: "success",
533
+ environment: "production",
534
+ branch: "main",
535
+ commit: "a1b2c3d4e5f6",
536
+ commitMessage: "feat: Add new dashboard component",
537
+ author: { name: "John Doe" },
538
+ startedAt: new Date(Date.now() - 3600000),
539
+ finishedAt: new Date(Date.now() - 3300000),
540
+ duration: "5m 12s",
541
+ url: "https://app.example.com",
542
+ },
543
+ {
544
+ id: "d2",
545
+ status: "building",
546
+ environment: "staging",
547
+ branch: "develop",
548
+ commit: "f6e5d4c3b2a1",
549
+ commitMessage: "fix: Resolve login issue",
550
+ author: { name: "Jane Smith" },
551
+ startedAt: new Date(Date.now() - 180000),
552
+ },
553
+ {
554
+ id: "d3",
555
+ status: "failed",
556
+ environment: "development",
557
+ branch: "feature/new-api",
558
+ commit: "1a2b3c4d5e6f",
559
+ commitMessage: "wip: New API endpoints",
560
+ author: { name: "Bob Wilson" },
561
+ startedAt: new Date(Date.now() - 7200000),
562
+ finishedAt: new Date(Date.now() - 7000000),
563
+ duration: "3m 20s",
564
+ },
565
+ ]
566
+
567
+ export const defaultPipelines: Pipeline[] = [
568
+ {
569
+ id: "p1",
570
+ name: "CI/CD Pipeline",
571
+ status: "building",
572
+ trigger: "push to main",
573
+ branch: "main",
574
+ startedAt: new Date(Date.now() - 300000),
575
+ stages: [
576
+ { id: "s1", name: "Build", status: "success", duration: "1m 30s" },
577
+ { id: "s2", name: "Test", status: "success", duration: "2m 45s" },
578
+ { id: "s3", name: "Deploy", status: "building" },
579
+ { id: "s4", name: "Verify", status: "pending" },
580
+ ],
581
+ },
582
+ ]
583
+
584
+ export const defaultEnvironments: EnvironmentStatus[] = [
585
+ {
586
+ name: "production",
587
+ status: "healthy",
588
+ url: "https://app.example.com",
589
+ uptime: "99.9%",
590
+ responseTime: "120ms",
591
+ lastDeployment: defaultDeployments[0],
592
+ },
593
+ {
594
+ name: "staging",
595
+ status: "degraded",
596
+ url: "https://staging.example.com",
597
+ uptime: "98.5%",
598
+ responseTime: "250ms",
599
+ },
600
+ {
601
+ name: "development",
602
+ status: "healthy",
603
+ url: "https://dev.example.com",
604
+ uptime: "95.0%",
605
+ responseTime: "180ms",
606
+ },
607
+ ]
608
+
609
+ export default DeploymentDashboard
@@ -78,3 +78,27 @@ export * from './wizard'
78
78
  // Theme & i18n
79
79
  // export * from './theme-creator-block' // TODO: Refactor to match new theme-provider API
80
80
  export * from './i18n-editor'
81
+
82
+ // New Blocks - KPI, E-commerce, Gaming, Security, Communication, DevOps
83
+ export * from './dashboard-kpi'
84
+ export * from './checkout-flow'
85
+ export * from './player-profile'
86
+ export * from './auth-2fa'
87
+
88
+ // Chat Interface - export with aliases to avoid conflicts with ./chat block
89
+ export {
90
+ ChatInterface,
91
+ defaultUsers as defaultChatInterfaceUsers,
92
+ defaultConversations as defaultChatInterfaceConversations,
93
+ defaultMessages as defaultChatInterfaceMessages,
94
+ } from './chat-interface'
95
+ export type {
96
+ ChatInterfaceProps,
97
+ ChatUser as ChatInterfaceUser,
98
+ ChatMessage as ChatInterfaceMessage,
99
+ ChatConversation as ChatInterfaceConversation,
100
+ ChatAttachment as ChatInterfaceAttachment,
101
+ ChatReaction as ChatInterfaceReaction,
102
+ } from './chat-interface'
103
+
104
+ export * from './deployment-dashboard'