@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.
- package/dist/blocks/apm-overview/index.d.ts +58 -0
- package/dist/blocks/cicd-builder/index.d.ts +47 -0
- package/dist/blocks/cloud-cost-dashboard/index.d.ts +49 -0
- package/dist/blocks/container-orchestrator/index.d.ts +63 -0
- package/dist/blocks/database-admin/index.d.ts +84 -0
- package/dist/blocks/gitops-sync-status/index.d.ts +45 -0
- package/dist/blocks/incident-manager/index.d.ts +44 -0
- package/dist/blocks/index.d.ts +10 -0
- package/dist/blocks/infrastructure-map/index.d.ts +32 -0
- package/dist/blocks/on-call-schedule/index.d.ts +43 -0
- package/dist/blocks/release-notes/index.d.ts +49 -0
- package/dist/components/index.d.ts +34 -0
- package/dist/components/waka-ad-banner/index.d.ts +36 -0
- package/dist/components/waka-ad-fallback/index.d.ts +33 -0
- package/dist/components/waka-ad-inline/index.d.ts +15 -0
- package/dist/components/waka-ad-interstitial/index.d.ts +26 -0
- package/dist/components/waka-ad-placeholder/index.d.ts +17 -0
- package/dist/components/waka-ad-provider/index.d.ts +103 -0
- package/dist/components/waka-ad-sidebar/index.d.ts +18 -0
- package/dist/components/waka-ad-sticky-footer/index.d.ts +17 -0
- package/dist/components/waka-alert-panel/index.d.ts +45 -0
- package/dist/components/waka-artifact-list/index.d.ts +32 -0
- package/dist/components/waka-build-matrix/index.d.ts +36 -0
- package/dist/components/waka-config-comparator/index.d.ts +37 -0
- package/dist/components/waka-container-list/index.d.ts +51 -0
- package/dist/components/waka-content-recommendation/index.d.ts +23 -0
- package/dist/components/waka-database-card/index.d.ts +46 -0
- package/dist/components/waka-dependency-tree/index.d.ts +38 -0
- package/dist/components/waka-env-var-editor/index.d.ts +30 -0
- package/dist/components/waka-feature-flag-row/index.d.ts +45 -0
- package/dist/components/waka-kubernetes-overview/index.d.ts +98 -0
- package/dist/components/waka-log-viewer/index.d.ts +38 -0
- package/dist/components/waka-migration-list/index.d.ts +36 -0
- package/dist/components/waka-outstream-video/index.d.ts +24 -0
- package/dist/components/waka-pod-card/index.d.ts +73 -0
- package/dist/components/waka-query-explain/index.d.ts +48 -0
- package/dist/components/waka-secret-card/index.d.ts +43 -0
- package/dist/components/waka-security-scan-result/index.d.ts +45 -0
- package/dist/components/waka-service-graph/index.d.ts +44 -0
- package/dist/components/waka-sponsored-badge/index.d.ts +20 -0
- package/dist/components/waka-sponsored-card/index.d.ts +25 -0
- package/dist/components/waka-sponsored-feed/index.d.ts +31 -0
- package/dist/components/waka-test-report/index.d.ts +60 -0
- package/dist/components/waka-trace-viewer/index.d.ts +36 -0
- package/dist/components/waka-video-ad/index.d.ts +32 -0
- package/dist/components/waka-video-overlay/index.d.ts +26 -0
- package/dist/index.cjs.js +251 -200
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +47315 -35823
- package/dist/utils/security.d.ts +96 -0
- package/package.json +4 -4
- package/src/blocks/apm-overview/index.tsx +672 -0
- package/src/blocks/cicd-builder/index.tsx +738 -0
- package/src/blocks/cloud-cost-dashboard/index.tsx +597 -0
- package/src/blocks/container-orchestrator/index.tsx +729 -0
- package/src/blocks/database-admin/index.tsx +679 -0
- package/src/blocks/gitops-sync-status/index.tsx +557 -0
- package/src/blocks/incident-manager/index.tsx +586 -0
- package/src/blocks/index.ts +119 -0
- package/src/blocks/infrastructure-map/index.tsx +638 -0
- package/src/blocks/on-call-schedule/index.tsx +615 -0
- package/src/blocks/release-notes/index.tsx +643 -0
- package/src/blocks/sidebar/index.tsx +6 -6
- package/src/components/DataTable/templates/index.tsx +3 -2
- package/src/components/index.ts +283 -0
- package/src/components/waka-3d-pie-chart/index.tsx +11 -11
- package/src/components/waka-achievement-unlock/index.tsx +16 -16
- package/src/components/waka-ad-banner/index.tsx +275 -0
- package/src/components/waka-ad-fallback/index.tsx +181 -0
- package/src/components/waka-ad-inline/index.tsx +103 -0
- package/src/components/waka-ad-interstitial/index.tsx +278 -0
- package/src/components/waka-ad-placeholder/index.tsx +84 -0
- package/src/components/waka-ad-provider/index.tsx +329 -0
- package/src/components/waka-ad-sidebar/index.tsx +113 -0
- package/src/components/waka-ad-sticky-footer/index.tsx +125 -0
- package/src/components/waka-alert-panel/index.tsx +493 -0
- package/src/components/waka-artifact-list/index.tsx +416 -0
- package/src/components/waka-badge-showcase/index.tsx +12 -11
- package/src/components/waka-build-matrix/index.tsx +396 -0
- package/src/components/waka-command-bar/index.tsx +2 -1
- package/src/components/waka-config-comparator/index.tsx +416 -0
- package/src/components/waka-container-list/index.tsx +475 -0
- package/src/components/waka-content-recommendation/index.tsx +294 -0
- package/src/components/waka-cost-breakdown/index.tsx +10 -10
- package/src/components/waka-database-card/index.tsx +473 -0
- package/src/components/waka-dependency-tree/index.tsx +542 -0
- package/src/components/waka-env-var-editor/index.tsx +417 -0
- package/src/components/waka-feature-flag-row/index.tsx +386 -0
- package/src/components/waka-funnel-chart/index.tsx +8 -8
- package/src/components/waka-health-pulse/index.tsx +6 -6
- package/src/components/waka-kubernetes-overview/index.tsx +536 -0
- package/src/components/waka-leaderboard/index.tsx +9 -9
- package/src/components/waka-log-viewer/index.tsx +386 -0
- package/src/components/waka-loot-box/index.tsx +20 -20
- package/src/components/waka-migration-list/index.tsx +487 -0
- package/src/components/waka-outstream-video/index.tsx +240 -0
- package/src/components/waka-player-card/index.tsx +5 -5
- package/src/components/waka-pod-card/index.tsx +528 -0
- package/src/components/waka-query-explain/index.tsx +657 -0
- package/src/components/waka-quota-bar/index.tsx +4 -4
- package/src/components/waka-radar-score/index.tsx +10 -10
- package/src/components/waka-scratch-card/index.tsx +5 -4
- package/src/components/waka-secret-card/index.tsx +371 -0
- package/src/components/waka-security-scan-result/index.tsx +473 -0
- package/src/components/waka-server-rack/index.tsx +28 -27
- package/src/components/waka-service-graph/index.tsx +445 -0
- package/src/components/waka-sponsored-badge/index.tsx +97 -0
- package/src/components/waka-sponsored-card/index.tsx +275 -0
- package/src/components/waka-sponsored-feed/index.tsx +127 -0
- package/src/components/waka-spotlight/index.tsx +2 -1
- package/src/components/waka-success-explosion/index.tsx +4 -4
- package/src/components/waka-test-report/index.tsx +469 -0
- package/src/components/waka-trace-viewer/index.tsx +490 -0
- package/src/components/waka-video-ad/index.tsx +406 -0
- package/src/components/waka-video-overlay/index.tsx +257 -0
- package/src/components/waka-xp-bar/index.tsx +13 -13
- package/src/styles/base.css +16 -0
- package/src/styles/tailwind.preset.js +12 -0
- package/src/styles/themes/forest.css +16 -0
- package/src/styles/themes/monochrome.css +16 -0
- package/src/styles/themes/perpetuity.css +16 -0
- package/src/styles/themes/sunset.css +16 -0
- package/src/styles/themes/twilight.css +16 -0
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../utils/cn"
|
|
5
|
+
import { Badge } from "../../components/badge"
|
|
6
|
+
import { Button } from "../../components/button"
|
|
7
|
+
import { Card, CardContent, CardHeader, CardTitle } from "../../components/card"
|
|
8
|
+
import { Input } from "../../components/input"
|
|
9
|
+
import { Textarea } from "../../components/textarea"
|
|
10
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../../components/avatar"
|
|
11
|
+
import { ScrollArea } from "../../components/scroll-area"
|
|
12
|
+
import {
|
|
13
|
+
Select,
|
|
14
|
+
SelectContent,
|
|
15
|
+
SelectItem,
|
|
16
|
+
SelectTrigger,
|
|
17
|
+
SelectValue,
|
|
18
|
+
} from "../../components/select"
|
|
19
|
+
import {
|
|
20
|
+
Tag,
|
|
21
|
+
GitCommit,
|
|
22
|
+
GitBranch,
|
|
23
|
+
GitPullRequest,
|
|
24
|
+
Plus,
|
|
25
|
+
Trash2,
|
|
26
|
+
Rocket,
|
|
27
|
+
Bug,
|
|
28
|
+
Sparkles,
|
|
29
|
+
Wrench,
|
|
30
|
+
AlertTriangle,
|
|
31
|
+
ShieldCheck,
|
|
32
|
+
Zap,
|
|
33
|
+
BookOpen,
|
|
34
|
+
Users,
|
|
35
|
+
Calendar,
|
|
36
|
+
Link,
|
|
37
|
+
ExternalLink,
|
|
38
|
+
Eye,
|
|
39
|
+
Edit,
|
|
40
|
+
Copy,
|
|
41
|
+
Download,
|
|
42
|
+
Clock,
|
|
43
|
+
CheckCircle2,
|
|
44
|
+
} from "lucide-react"
|
|
45
|
+
|
|
46
|
+
export type ChangeType = "feature" | "fix" | "improvement" | "breaking" | "security" | "performance" | "docs" | "deprecation"
|
|
47
|
+
export type ReleaseType = "major" | "minor" | "patch" | "prerelease"
|
|
48
|
+
export type ReleaseStatus = "draft" | "published" | "scheduled"
|
|
49
|
+
|
|
50
|
+
export interface Contributor {
|
|
51
|
+
id: string
|
|
52
|
+
name: string
|
|
53
|
+
username: string
|
|
54
|
+
avatar?: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ChangeItem {
|
|
58
|
+
id: string
|
|
59
|
+
type: ChangeType
|
|
60
|
+
title: string
|
|
61
|
+
description?: string
|
|
62
|
+
pullRequest?: {
|
|
63
|
+
number: number
|
|
64
|
+
url: string
|
|
65
|
+
}
|
|
66
|
+
commits?: string[]
|
|
67
|
+
contributors?: Contributor[]
|
|
68
|
+
breaking?: boolean
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface Release {
|
|
72
|
+
id: string
|
|
73
|
+
version: string
|
|
74
|
+
name?: string
|
|
75
|
+
releaseType: ReleaseType
|
|
76
|
+
status: ReleaseStatus
|
|
77
|
+
publishedAt?: Date
|
|
78
|
+
scheduledAt?: Date
|
|
79
|
+
createdAt: Date
|
|
80
|
+
changes: ChangeItem[]
|
|
81
|
+
summary?: string
|
|
82
|
+
upgradeNotes?: string
|
|
83
|
+
contributors: Contributor[]
|
|
84
|
+
compareUrl?: string
|
|
85
|
+
downloadUrl?: string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface ReleaseNotesProps {
|
|
89
|
+
releases: Release[]
|
|
90
|
+
onCreateRelease?: () => void
|
|
91
|
+
onEditRelease?: (release: Release) => void
|
|
92
|
+
onPublishRelease?: (release: Release) => void
|
|
93
|
+
onAddChange?: (releaseId: string, change: ChangeItem) => void
|
|
94
|
+
showEditor?: boolean
|
|
95
|
+
className?: string
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const changeTypeConfig: Record<ChangeType, { icon: React.ElementType; color: string; bgColor: string; label: string }> = {
|
|
99
|
+
feature: { icon: Sparkles, color: "text-green-500", bgColor: "bg-green-500/10", label: "New Feature" },
|
|
100
|
+
fix: { icon: Bug, color: "text-red-500", bgColor: "bg-red-500/10", label: "Bug Fix" },
|
|
101
|
+
improvement: { icon: Wrench, color: "text-blue-500", bgColor: "bg-blue-500/10", label: "Improvement" },
|
|
102
|
+
breaking: { icon: AlertTriangle, color: "text-orange-500", bgColor: "bg-orange-500/10", label: "Breaking Change" },
|
|
103
|
+
security: { icon: ShieldCheck, color: "text-purple-500", bgColor: "bg-purple-500/10", label: "Security" },
|
|
104
|
+
performance: { icon: Zap, color: "text-yellow-500", bgColor: "bg-yellow-500/10", label: "Performance" },
|
|
105
|
+
docs: { icon: BookOpen, color: "text-cyan-500", bgColor: "bg-cyan-500/10", label: "Documentation" },
|
|
106
|
+
deprecation: { icon: AlertTriangle, color: "text-gray-500", bgColor: "bg-gray-500/10", label: "Deprecation" },
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const releaseTypeConfig: Record<ReleaseType, { label: string; color: string }> = {
|
|
110
|
+
major: { label: "Major", color: "text-red-500" },
|
|
111
|
+
minor: { label: "Minor", color: "text-blue-500" },
|
|
112
|
+
patch: { label: "Patch", color: "text-green-500" },
|
|
113
|
+
prerelease: { label: "Pre-release", color: "text-purple-500" },
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function formatDate(date: Date): string {
|
|
117
|
+
return date.toLocaleDateString("en-US", {
|
|
118
|
+
year: "numeric",
|
|
119
|
+
month: "long",
|
|
120
|
+
day: "numeric",
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function ChangeEntry({ change }: { change: ChangeItem }) {
|
|
125
|
+
const config = changeTypeConfig[change.type]
|
|
126
|
+
const Icon = config.icon
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div className={cn(
|
|
130
|
+
"flex items-start gap-3 p-3 rounded-lg",
|
|
131
|
+
config.bgColor
|
|
132
|
+
)}>
|
|
133
|
+
<div className="p-1.5 rounded bg-background">
|
|
134
|
+
<Icon className={cn("h-4 w-4", config.color)} />
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div className="flex-1 min-w-0">
|
|
138
|
+
<div className="flex items-center gap-2 flex-wrap">
|
|
139
|
+
<span className="font-medium">{change.title}</span>
|
|
140
|
+
{change.breaking && (
|
|
141
|
+
<Badge className="bg-orange-500 text-xs">Breaking</Badge>
|
|
142
|
+
)}
|
|
143
|
+
{change.pullRequest && (
|
|
144
|
+
<a
|
|
145
|
+
href={change.pullRequest.url}
|
|
146
|
+
target="_blank"
|
|
147
|
+
rel="noopener noreferrer"
|
|
148
|
+
className="text-xs text-muted-foreground hover:text-primary flex items-center gap-1"
|
|
149
|
+
>
|
|
150
|
+
<GitPullRequest className="h-3 w-3" />
|
|
151
|
+
#{change.pullRequest.number}
|
|
152
|
+
</a>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
{change.description && (
|
|
157
|
+
<p className="text-sm text-muted-foreground mt-1">
|
|
158
|
+
{change.description}
|
|
159
|
+
</p>
|
|
160
|
+
)}
|
|
161
|
+
|
|
162
|
+
{change.contributors && change.contributors.length > 0 && (
|
|
163
|
+
<div className="flex items-center gap-1 mt-2">
|
|
164
|
+
{change.contributors.map((contributor) => (
|
|
165
|
+
<Avatar key={contributor.id} className="h-5 w-5">
|
|
166
|
+
<AvatarImage src={contributor.avatar} />
|
|
167
|
+
<AvatarFallback className="text-xs">
|
|
168
|
+
{contributor.name.slice(0, 2).toUpperCase()}
|
|
169
|
+
</AvatarFallback>
|
|
170
|
+
</Avatar>
|
|
171
|
+
))}
|
|
172
|
+
</div>
|
|
173
|
+
)}
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function ReleaseCard({
|
|
180
|
+
release,
|
|
181
|
+
onEdit,
|
|
182
|
+
onPublish,
|
|
183
|
+
expanded = false,
|
|
184
|
+
}: {
|
|
185
|
+
release: Release
|
|
186
|
+
onEdit?: () => void
|
|
187
|
+
onPublish?: () => void
|
|
188
|
+
expanded?: boolean
|
|
189
|
+
}) {
|
|
190
|
+
const [isExpanded, setIsExpanded] = React.useState(expanded)
|
|
191
|
+
const releaseConf = releaseTypeConfig[release.releaseType]
|
|
192
|
+
|
|
193
|
+
// Group changes by type
|
|
194
|
+
const groupedChanges = React.useMemo(() => {
|
|
195
|
+
const groups: Record<ChangeType, ChangeItem[]> = {
|
|
196
|
+
breaking: [],
|
|
197
|
+
security: [],
|
|
198
|
+
feature: [],
|
|
199
|
+
improvement: [],
|
|
200
|
+
performance: [],
|
|
201
|
+
fix: [],
|
|
202
|
+
docs: [],
|
|
203
|
+
deprecation: [],
|
|
204
|
+
}
|
|
205
|
+
release.changes.forEach((change) => {
|
|
206
|
+
groups[change.type].push(change)
|
|
207
|
+
})
|
|
208
|
+
return groups
|
|
209
|
+
}, [release.changes])
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<Card className={cn(
|
|
213
|
+
release.status === "draft" && "border-dashed",
|
|
214
|
+
release.status === "scheduled" && "border-blue-500/30"
|
|
215
|
+
)}>
|
|
216
|
+
<CardHeader className="pb-3">
|
|
217
|
+
<div className="flex items-start justify-between">
|
|
218
|
+
<div className="space-y-2">
|
|
219
|
+
<div className="flex items-center gap-2">
|
|
220
|
+
<Tag className="h-5 w-5" />
|
|
221
|
+
<CardTitle className="text-xl">{release.version}</CardTitle>
|
|
222
|
+
{release.name && (
|
|
223
|
+
<span className="text-muted-foreground">- {release.name}</span>
|
|
224
|
+
)}
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<div className="flex items-center gap-2">
|
|
228
|
+
<Badge variant="outline" className={releaseConf.color}>
|
|
229
|
+
{releaseConf.label}
|
|
230
|
+
</Badge>
|
|
231
|
+
<Badge
|
|
232
|
+
variant={release.status === "published" ? "default" : "secondary"}
|
|
233
|
+
className={release.status === "published" ? "bg-green-500" : ""}
|
|
234
|
+
>
|
|
235
|
+
{release.status === "published" ? (
|
|
236
|
+
<CheckCircle2 className="h-3 w-3 mr-1" />
|
|
237
|
+
) : release.status === "scheduled" ? (
|
|
238
|
+
<Clock className="h-3 w-3 mr-1" />
|
|
239
|
+
) : (
|
|
240
|
+
<Edit className="h-3 w-3 mr-1" />
|
|
241
|
+
)}
|
|
242
|
+
{release.status}
|
|
243
|
+
</Badge>
|
|
244
|
+
<span className="text-sm text-muted-foreground flex items-center gap-1">
|
|
245
|
+
<Calendar className="h-3 w-3" />
|
|
246
|
+
{release.publishedAt ? formatDate(release.publishedAt) : formatDate(release.createdAt)}
|
|
247
|
+
</span>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<div className="flex items-center gap-2">
|
|
252
|
+
{release.compareUrl && (
|
|
253
|
+
<Button variant="ghost" size="sm" asChild>
|
|
254
|
+
<a href={release.compareUrl} target="_blank" rel="noopener noreferrer">
|
|
255
|
+
<GitCommit className="h-4 w-4 mr-1" />
|
|
256
|
+
Compare
|
|
257
|
+
</a>
|
|
258
|
+
</Button>
|
|
259
|
+
)}
|
|
260
|
+
{onEdit && release.status === "draft" && (
|
|
261
|
+
<Button variant="outline" size="sm" onClick={onEdit}>
|
|
262
|
+
<Edit className="h-4 w-4 mr-1" />
|
|
263
|
+
Edit
|
|
264
|
+
</Button>
|
|
265
|
+
)}
|
|
266
|
+
{onPublish && release.status === "draft" && (
|
|
267
|
+
<Button size="sm" onClick={onPublish}>
|
|
268
|
+
<Rocket className="h-4 w-4 mr-1" />
|
|
269
|
+
Publish
|
|
270
|
+
</Button>
|
|
271
|
+
)}
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</CardHeader>
|
|
275
|
+
|
|
276
|
+
<CardContent className="space-y-4">
|
|
277
|
+
{/* Summary */}
|
|
278
|
+
{release.summary && (
|
|
279
|
+
<p className="text-muted-foreground">{release.summary}</p>
|
|
280
|
+
)}
|
|
281
|
+
|
|
282
|
+
{/* Upgrade Notes */}
|
|
283
|
+
{release.upgradeNotes && (
|
|
284
|
+
<div className="p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-lg">
|
|
285
|
+
<div className="flex items-center gap-2 font-medium text-yellow-600 mb-1">
|
|
286
|
+
<AlertTriangle className="h-4 w-4" />
|
|
287
|
+
Upgrade Notes
|
|
288
|
+
</div>
|
|
289
|
+
<p className="text-sm text-muted-foreground">{release.upgradeNotes}</p>
|
|
290
|
+
</div>
|
|
291
|
+
)}
|
|
292
|
+
|
|
293
|
+
{/* Changes */}
|
|
294
|
+
<div className="space-y-4">
|
|
295
|
+
{Object.entries(groupedChanges).map(([type, changes]) => {
|
|
296
|
+
if (changes.length === 0) return null
|
|
297
|
+
const config = changeTypeConfig[type as ChangeType]
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
<div key={type}>
|
|
301
|
+
<h4 className="font-medium flex items-center gap-2 mb-2">
|
|
302
|
+
<config.icon className={cn("h-4 w-4", config.color)} />
|
|
303
|
+
{config.label}s ({changes.length})
|
|
304
|
+
</h4>
|
|
305
|
+
<div className="space-y-2">
|
|
306
|
+
{(isExpanded ? changes : changes.slice(0, 3)).map((change) => (
|
|
307
|
+
<ChangeEntry key={change.id} change={change} />
|
|
308
|
+
))}
|
|
309
|
+
{!isExpanded && changes.length > 3 && (
|
|
310
|
+
<Button
|
|
311
|
+
variant="ghost"
|
|
312
|
+
size="sm"
|
|
313
|
+
className="w-full"
|
|
314
|
+
onClick={() => setIsExpanded(true)}
|
|
315
|
+
>
|
|
316
|
+
Show {changes.length - 3} more...
|
|
317
|
+
</Button>
|
|
318
|
+
)}
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
)
|
|
322
|
+
})}
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
{/* Contributors */}
|
|
326
|
+
{release.contributors.length > 0 && (
|
|
327
|
+
<div className="pt-4 border-t">
|
|
328
|
+
<h4 className="font-medium flex items-center gap-2 mb-2">
|
|
329
|
+
<Users className="h-4 w-4" />
|
|
330
|
+
Contributors ({release.contributors.length})
|
|
331
|
+
</h4>
|
|
332
|
+
<div className="flex flex-wrap gap-2">
|
|
333
|
+
{release.contributors.map((contributor) => (
|
|
334
|
+
<div
|
|
335
|
+
key={contributor.id}
|
|
336
|
+
className="flex items-center gap-2 px-2 py-1 bg-muted rounded-full"
|
|
337
|
+
>
|
|
338
|
+
<Avatar className="h-5 w-5">
|
|
339
|
+
<AvatarImage src={contributor.avatar} />
|
|
340
|
+
<AvatarFallback className="text-xs">
|
|
341
|
+
{contributor.name.slice(0, 2).toUpperCase()}
|
|
342
|
+
</AvatarFallback>
|
|
343
|
+
</Avatar>
|
|
344
|
+
<span className="text-sm">@{contributor.username}</span>
|
|
345
|
+
</div>
|
|
346
|
+
))}
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
)}
|
|
350
|
+
|
|
351
|
+
{/* Download */}
|
|
352
|
+
{release.downloadUrl && (
|
|
353
|
+
<div className="pt-4 border-t">
|
|
354
|
+
<Button variant="outline" asChild>
|
|
355
|
+
<a href={release.downloadUrl} download>
|
|
356
|
+
<Download className="h-4 w-4 mr-2" />
|
|
357
|
+
Download Release
|
|
358
|
+
</a>
|
|
359
|
+
</Button>
|
|
360
|
+
</div>
|
|
361
|
+
)}
|
|
362
|
+
</CardContent>
|
|
363
|
+
</Card>
|
|
364
|
+
)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function ReleaseNotes({
|
|
368
|
+
releases,
|
|
369
|
+
onCreateRelease,
|
|
370
|
+
onEditRelease,
|
|
371
|
+
onPublishRelease,
|
|
372
|
+
onAddChange,
|
|
373
|
+
showEditor = false,
|
|
374
|
+
className,
|
|
375
|
+
}: ReleaseNotesProps) {
|
|
376
|
+
const [filterType, setFilterType] = React.useState<ReleaseType | "all">("all")
|
|
377
|
+
const [filterStatus, setFilterStatus] = React.useState<ReleaseStatus | "all">("all")
|
|
378
|
+
|
|
379
|
+
// Filter releases
|
|
380
|
+
const filteredReleases = React.useMemo(() => {
|
|
381
|
+
return releases
|
|
382
|
+
.filter((r) => {
|
|
383
|
+
if (filterType !== "all" && r.releaseType !== filterType) return false
|
|
384
|
+
if (filterStatus !== "all" && r.status !== filterStatus) return false
|
|
385
|
+
return true
|
|
386
|
+
})
|
|
387
|
+
.sort((a, b) => {
|
|
388
|
+
// Sort by version (semantic versioning)
|
|
389
|
+
const parseVersion = (v: string) => {
|
|
390
|
+
const parts = v.replace(/^v/, "").split(/[.-]/)
|
|
391
|
+
return parts.map((p) => parseInt(p) || 0)
|
|
392
|
+
}
|
|
393
|
+
const aVer = parseVersion(a.version)
|
|
394
|
+
const bVer = parseVersion(b.version)
|
|
395
|
+
for (let i = 0; i < Math.max(aVer.length, bVer.length); i++) {
|
|
396
|
+
const diff = (bVer[i] || 0) - (aVer[i] || 0)
|
|
397
|
+
if (diff !== 0) return diff
|
|
398
|
+
}
|
|
399
|
+
return 0
|
|
400
|
+
})
|
|
401
|
+
}, [releases, filterType, filterStatus])
|
|
402
|
+
|
|
403
|
+
// Stats
|
|
404
|
+
const stats = React.useMemo(() => {
|
|
405
|
+
return {
|
|
406
|
+
total: releases.length,
|
|
407
|
+
published: releases.filter((r) => r.status === "published").length,
|
|
408
|
+
draft: releases.filter((r) => r.status === "draft").length,
|
|
409
|
+
features: releases.reduce((acc, r) => acc + r.changes.filter((c) => c.type === "feature").length, 0),
|
|
410
|
+
fixes: releases.reduce((acc, r) => acc + r.changes.filter((c) => c.type === "fix").length, 0),
|
|
411
|
+
}
|
|
412
|
+
}, [releases])
|
|
413
|
+
|
|
414
|
+
return (
|
|
415
|
+
<div className={cn("space-y-6", className)}>
|
|
416
|
+
{/* Header */}
|
|
417
|
+
<Card>
|
|
418
|
+
<CardHeader>
|
|
419
|
+
<div className="flex items-center justify-between">
|
|
420
|
+
<div className="flex items-center gap-3">
|
|
421
|
+
<Rocket className="h-6 w-6" />
|
|
422
|
+
<div>
|
|
423
|
+
<CardTitle>Release Notes</CardTitle>
|
|
424
|
+
<p className="text-sm text-muted-foreground">
|
|
425
|
+
Track changes and publish releases
|
|
426
|
+
</p>
|
|
427
|
+
</div>
|
|
428
|
+
</div>
|
|
429
|
+
|
|
430
|
+
<div className="flex items-center gap-4">
|
|
431
|
+
<div className="flex items-center gap-6 text-sm">
|
|
432
|
+
<span className="flex items-center gap-1">
|
|
433
|
+
<Tag className="h-4 w-4 text-muted-foreground" />
|
|
434
|
+
<strong>{stats.total}</strong> releases
|
|
435
|
+
</span>
|
|
436
|
+
<span className="flex items-center gap-1">
|
|
437
|
+
<Sparkles className="h-4 w-4 text-green-500" />
|
|
438
|
+
<strong>{stats.features}</strong> features
|
|
439
|
+
</span>
|
|
440
|
+
<span className="flex items-center gap-1">
|
|
441
|
+
<Bug className="h-4 w-4 text-red-500" />
|
|
442
|
+
<strong>{stats.fixes}</strong> fixes
|
|
443
|
+
</span>
|
|
444
|
+
</div>
|
|
445
|
+
|
|
446
|
+
{onCreateRelease && (
|
|
447
|
+
<Button onClick={onCreateRelease}>
|
|
448
|
+
<Plus className="h-4 w-4 mr-1" />
|
|
449
|
+
New Release
|
|
450
|
+
</Button>
|
|
451
|
+
)}
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
</CardHeader>
|
|
455
|
+
|
|
456
|
+
<CardContent className="pt-0">
|
|
457
|
+
<div className="flex items-center gap-3">
|
|
458
|
+
<Select value={filterType} onValueChange={(v) => setFilterType(v as ReleaseType | "all")}>
|
|
459
|
+
<SelectTrigger className="w-32">
|
|
460
|
+
<SelectValue placeholder="Type" />
|
|
461
|
+
</SelectTrigger>
|
|
462
|
+
<SelectContent>
|
|
463
|
+
<SelectItem value="all">All Types</SelectItem>
|
|
464
|
+
<SelectItem value="major">Major</SelectItem>
|
|
465
|
+
<SelectItem value="minor">Minor</SelectItem>
|
|
466
|
+
<SelectItem value="patch">Patch</SelectItem>
|
|
467
|
+
<SelectItem value="prerelease">Pre-release</SelectItem>
|
|
468
|
+
</SelectContent>
|
|
469
|
+
</Select>
|
|
470
|
+
|
|
471
|
+
<Select value={filterStatus} onValueChange={(v) => setFilterStatus(v as ReleaseStatus | "all")}>
|
|
472
|
+
<SelectTrigger className="w-32">
|
|
473
|
+
<SelectValue placeholder="Status" />
|
|
474
|
+
</SelectTrigger>
|
|
475
|
+
<SelectContent>
|
|
476
|
+
<SelectItem value="all">All Status</SelectItem>
|
|
477
|
+
<SelectItem value="published">Published</SelectItem>
|
|
478
|
+
<SelectItem value="draft">Draft</SelectItem>
|
|
479
|
+
<SelectItem value="scheduled">Scheduled</SelectItem>
|
|
480
|
+
</SelectContent>
|
|
481
|
+
</Select>
|
|
482
|
+
</div>
|
|
483
|
+
</CardContent>
|
|
484
|
+
</Card>
|
|
485
|
+
|
|
486
|
+
{/* Releases */}
|
|
487
|
+
<ScrollArea className="h-[600px]">
|
|
488
|
+
<div className="space-y-4 pr-4">
|
|
489
|
+
{filteredReleases.length === 0 ? (
|
|
490
|
+
<div className="flex flex-col items-center justify-center h-48 text-muted-foreground">
|
|
491
|
+
<Tag className="h-12 w-12 mb-4" />
|
|
492
|
+
<p className="text-lg font-medium">No releases found</p>
|
|
493
|
+
<p className="text-sm">Create your first release to get started</p>
|
|
494
|
+
</div>
|
|
495
|
+
) : (
|
|
496
|
+
filteredReleases.map((release, index) => (
|
|
497
|
+
<ReleaseCard
|
|
498
|
+
key={release.id}
|
|
499
|
+
release={release}
|
|
500
|
+
expanded={index === 0}
|
|
501
|
+
onEdit={onEditRelease ? () => onEditRelease(release) : undefined}
|
|
502
|
+
onPublish={onPublishRelease ? () => onPublishRelease(release) : undefined}
|
|
503
|
+
/>
|
|
504
|
+
))
|
|
505
|
+
)}
|
|
506
|
+
</div>
|
|
507
|
+
</ScrollArea>
|
|
508
|
+
</div>
|
|
509
|
+
)
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Default sample data
|
|
513
|
+
const sampleContributors: Contributor[] = [
|
|
514
|
+
{ id: "1", name: "John Doe", username: "johndoe", avatar: "" },
|
|
515
|
+
{ id: "2", name: "Jane Smith", username: "janesmith", avatar: "" },
|
|
516
|
+
{ id: "3", name: "Bob Wilson", username: "bobwilson", avatar: "" },
|
|
517
|
+
]
|
|
518
|
+
|
|
519
|
+
export const defaultReleases: Release[] = [
|
|
520
|
+
{
|
|
521
|
+
id: "1",
|
|
522
|
+
version: "v2.5.0",
|
|
523
|
+
name: "Aurora",
|
|
524
|
+
releaseType: "minor",
|
|
525
|
+
status: "published",
|
|
526
|
+
publishedAt: new Date(Date.now() - 2 * 24 * 3600000),
|
|
527
|
+
createdAt: new Date(Date.now() - 5 * 24 * 3600000),
|
|
528
|
+
summary: "This release introduces new authentication features, performance improvements, and several bug fixes.",
|
|
529
|
+
contributors: sampleContributors,
|
|
530
|
+
compareUrl: "https://github.com/example/repo/compare/v2.4.0...v2.5.0",
|
|
531
|
+
changes: [
|
|
532
|
+
{
|
|
533
|
+
id: "c1",
|
|
534
|
+
type: "feature",
|
|
535
|
+
title: "Added OAuth 2.0 support for GitHub and Google",
|
|
536
|
+
description: "Users can now sign in using their GitHub or Google accounts",
|
|
537
|
+
pullRequest: { number: 234, url: "#" },
|
|
538
|
+
contributors: [sampleContributors[0]],
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
id: "c2",
|
|
542
|
+
type: "feature",
|
|
543
|
+
title: "New dashboard widgets for analytics",
|
|
544
|
+
pullRequest: { number: 238, url: "#" },
|
|
545
|
+
contributors: [sampleContributors[1]],
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
id: "c3",
|
|
549
|
+
type: "improvement",
|
|
550
|
+
title: "Redesigned settings page with better UX",
|
|
551
|
+
pullRequest: { number: 241, url: "#" },
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
id: "c4",
|
|
555
|
+
type: "performance",
|
|
556
|
+
title: "Reduced initial bundle size by 30%",
|
|
557
|
+
description: "Implemented code splitting and lazy loading for major routes",
|
|
558
|
+
pullRequest: { number: 245, url: "#" },
|
|
559
|
+
contributors: [sampleContributors[2]],
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
id: "c5",
|
|
563
|
+
type: "fix",
|
|
564
|
+
title: "Fixed memory leak in WebSocket connection",
|
|
565
|
+
pullRequest: { number: 250, url: "#" },
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
id: "c6",
|
|
569
|
+
type: "fix",
|
|
570
|
+
title: "Fixed date picker not respecting timezone",
|
|
571
|
+
pullRequest: { number: 252, url: "#" },
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
id: "c7",
|
|
575
|
+
type: "security",
|
|
576
|
+
title: "Updated dependencies with security vulnerabilities",
|
|
577
|
+
description: "Updated axios, lodash, and other packages with known CVEs",
|
|
578
|
+
pullRequest: { number: 248, url: "#" },
|
|
579
|
+
},
|
|
580
|
+
],
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
id: "2",
|
|
584
|
+
version: "v2.4.1",
|
|
585
|
+
releaseType: "patch",
|
|
586
|
+
status: "published",
|
|
587
|
+
publishedAt: new Date(Date.now() - 14 * 24 * 3600000),
|
|
588
|
+
createdAt: new Date(Date.now() - 15 * 24 * 3600000),
|
|
589
|
+
contributors: [sampleContributors[0]],
|
|
590
|
+
changes: [
|
|
591
|
+
{
|
|
592
|
+
id: "c8",
|
|
593
|
+
type: "fix",
|
|
594
|
+
title: "Fixed critical authentication bypass vulnerability",
|
|
595
|
+
breaking: false,
|
|
596
|
+
pullRequest: { number: 230, url: "#" },
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
id: "c9",
|
|
600
|
+
type: "fix",
|
|
601
|
+
title: "Fixed incorrect pagination in search results",
|
|
602
|
+
pullRequest: { number: 231, url: "#" },
|
|
603
|
+
},
|
|
604
|
+
],
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
id: "3",
|
|
608
|
+
version: "v3.0.0-beta.1",
|
|
609
|
+
name: "Next Generation",
|
|
610
|
+
releaseType: "prerelease",
|
|
611
|
+
status: "draft",
|
|
612
|
+
createdAt: new Date(Date.now() - 1 * 24 * 3600000),
|
|
613
|
+
contributors: sampleContributors,
|
|
614
|
+
upgradeNotes: "This is a major release with breaking changes. Please review the migration guide before upgrading.",
|
|
615
|
+
changes: [
|
|
616
|
+
{
|
|
617
|
+
id: "c10",
|
|
618
|
+
type: "breaking",
|
|
619
|
+
title: "Removed deprecated API endpoints",
|
|
620
|
+
description: "The v1 API endpoints have been removed. Please migrate to v2 endpoints.",
|
|
621
|
+
breaking: true,
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
id: "c11",
|
|
625
|
+
type: "feature",
|
|
626
|
+
title: "New real-time collaboration features",
|
|
627
|
+
description: "Multiple users can now edit documents simultaneously with live cursors",
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
id: "c12",
|
|
631
|
+
type: "feature",
|
|
632
|
+
title: "Dark mode support throughout the application",
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
id: "c13",
|
|
636
|
+
type: "breaking",
|
|
637
|
+
title: "Changed authentication token format",
|
|
638
|
+
description: "Tokens now use JWT format. Old tokens will be invalidated.",
|
|
639
|
+
breaking: true,
|
|
640
|
+
},
|
|
641
|
+
],
|
|
642
|
+
},
|
|
643
|
+
]
|
|
@@ -554,13 +554,13 @@ export function WakaSidebar({
|
|
|
554
554
|
onClose: () => setIsOpen(false),
|
|
555
555
|
}
|
|
556
556
|
|
|
557
|
-
// Styles personnalisés
|
|
557
|
+
// Styles personnalisés - utilise les variables CSS du thème par défaut
|
|
558
558
|
const customStyles = {
|
|
559
|
-
"--sidebar-bg": backgroundColor || "hsl(
|
|
560
|
-
"--sidebar-text": textColor || "hsl(
|
|
561
|
-
"--sidebar-active": activeColor || "hsl(
|
|
562
|
-
"--sidebar-active-foreground": "hsl(
|
|
563
|
-
"--sidebar-hover": hoverColor || "
|
|
559
|
+
"--sidebar-bg": backgroundColor || "hsl(var(--sidebar-background, var(--card)))",
|
|
560
|
+
"--sidebar-text": textColor || "hsl(var(--sidebar-foreground, var(--card-foreground)))",
|
|
561
|
+
"--sidebar-active": activeColor || "hsl(var(--sidebar-primary, var(--primary)))",
|
|
562
|
+
"--sidebar-active-foreground": "hsl(var(--sidebar-primary-foreground, var(--primary-foreground)))",
|
|
563
|
+
"--sidebar-hover": hoverColor || "hsl(var(--sidebar-accent, var(--accent)))",
|
|
564
564
|
} as React.CSSProperties
|
|
565
565
|
|
|
566
566
|
const sidebarClasses = cn(
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
AlertDialogTitle,
|
|
29
29
|
} from "../../alert-dialog"
|
|
30
30
|
import { cn } from "../../../utils/cn"
|
|
31
|
+
import { createHighlightRegex } from "../../../utils/security"
|
|
31
32
|
import { formatters } from "../formatters"
|
|
32
33
|
import type { ColumnTemplate, ColumnTemplateOptions, ColumnTemplateAction } from "../types"
|
|
33
34
|
|
|
@@ -715,9 +716,9 @@ export const textTemplate: ColumnTemplate<unknown, unknown> = {
|
|
|
715
716
|
? formatters.truncate(strValue, options.maxLength)
|
|
716
717
|
: strValue
|
|
717
718
|
|
|
718
|
-
// Highlight si spécifié
|
|
719
|
+
// Highlight si spécifié (with escaped regex for security)
|
|
719
720
|
if (options?.highlight) {
|
|
720
|
-
const regex =
|
|
721
|
+
const regex = createHighlightRegex(options.highlight)
|
|
721
722
|
const parts = displayValue.split(regex)
|
|
722
723
|
|
|
723
724
|
return (
|