@wakastellar/ui 2.4.0 → 3.1.0

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 (173) hide show
  1. package/dist/blocks/antivirus-dashboard/index.d.ts +44 -0
  2. package/dist/blocks/clamav-service-status/index.d.ts +35 -0
  3. package/dist/blocks/file-scan-uploader/index.d.ts +29 -0
  4. package/dist/blocks/index.d.ts +18 -9
  5. package/dist/blocks/quarantine-manager/index.d.ts +27 -0
  6. package/dist/blocks/scan-history-log/index.d.ts +28 -0
  7. package/dist/blocks/scan-policy-editor/index.d.ts +27 -0
  8. package/dist/blocks/scan-report-generator/index.d.ts +47 -0
  9. package/dist/blocks/signature-database-manager/index.d.ts +39 -0
  10. package/dist/blocks/threat-alert-banner/index.d.ts +26 -0
  11. package/dist/components/index.d.ts +4 -4
  12. package/dist/components/waka-signature-pad/index.d.ts +1 -1
  13. package/dist/exceljs.min-BcLLX0PC.js +29 -0
  14. package/dist/exceljs.min-KOayaaQ4.mjs +23013 -0
  15. package/dist/export.cjs.js +1 -1
  16. package/dist/export.d.ts +2 -2
  17. package/dist/export.es.js +1 -1
  18. package/dist/index.cjs.js +136 -136
  19. package/dist/index.es.js +29978 -27215
  20. package/dist/stories/Button.stories.d.ts +1 -1
  21. package/dist/stories/Header.stories.d.ts +1 -1
  22. package/dist/stories/Page.stories.d.ts +1 -1
  23. package/dist/useDataTableImport-COVnvslz.js +9 -0
  24. package/dist/useDataTableImport-DAlxBY8w.mjs +237 -0
  25. package/dist/utils/index.d.ts +1 -0
  26. package/dist/utils/logger.d.ts +9 -0
  27. package/package.json +6 -5
  28. package/src/blocks/antivirus-dashboard/AntivirusDashboard.stories.tsx +291 -0
  29. package/src/blocks/antivirus-dashboard/index.tsx +525 -0
  30. package/src/blocks/clamav-service-status/ClamAVServiceStatus.stories.tsx +195 -0
  31. package/src/blocks/clamav-service-status/index.tsx +370 -0
  32. package/src/blocks/file-scan-uploader/FileScanUploader.stories.tsx +257 -0
  33. package/src/blocks/file-scan-uploader/index.tsx +311 -0
  34. package/src/blocks/index.ts +163 -11
  35. package/src/blocks/quarantine-manager/QuarantineManager.stories.tsx +209 -0
  36. package/src/blocks/quarantine-manager/index.tsx +435 -0
  37. package/src/blocks/scan-history-log/ScanHistoryLog.stories.tsx +231 -0
  38. package/src/blocks/scan-history-log/index.tsx +406 -0
  39. package/src/blocks/scan-policy-editor/ScanPolicyEditor.stories.tsx +106 -0
  40. package/src/blocks/scan-policy-editor/index.tsx +418 -0
  41. package/src/blocks/scan-report-generator/ScanReportGenerator.stories.tsx +232 -0
  42. package/src/blocks/scan-report-generator/index.tsx +612 -0
  43. package/src/blocks/sidebar/index.tsx +2 -1
  44. package/src/blocks/signature-database-manager/SignatureDatabaseManager.stories.tsx +279 -0
  45. package/src/blocks/signature-database-manager/index.tsx +470 -0
  46. package/src/blocks/theme-creator-block/index.tsx +16 -2
  47. package/src/blocks/threat-alert-banner/ThreatAlertBanner.stories.tsx +152 -0
  48. package/src/blocks/threat-alert-banner/index.tsx +320 -0
  49. package/src/components/DataTable/DataTable.stories.tsx +203 -0
  50. package/src/components/DataTable/hooks/useDataTableExport.ts +38 -31
  51. package/src/components/DataTable/hooks/useDataTableImport.ts +31 -20
  52. package/src/components/error-boundary/ErrorBoundary.stories.tsx +125 -0
  53. package/src/components/index.ts +45 -4
  54. package/src/components/language-selector/LanguageSelector.stories.tsx +112 -0
  55. package/src/components/theme-selector/ThemeSelector.stories.tsx +77 -0
  56. package/src/components/toaster/Toaster.stories.tsx +67 -0
  57. package/src/components/waka-activity-feed/WakaActivityFeed.stories.tsx +116 -0
  58. package/src/components/waka-ad-banner/WakaAdBanner.stories.tsx +102 -0
  59. package/src/components/waka-ad-fallback/WakaAdFallback.stories.tsx +117 -0
  60. package/src/components/waka-ad-inline/WakaAdInline.stories.tsx +105 -0
  61. package/src/components/waka-ad-interstitial/WakaAdInterstitial.stories.tsx +92 -0
  62. package/src/components/waka-ad-placeholder/WakaAdPlaceholder.stories.tsx +89 -0
  63. package/src/components/waka-ad-provider/WakaAdProvider.stories.tsx +110 -0
  64. package/src/components/waka-ad-sidebar/WakaAdSidebar.stories.tsx +89 -0
  65. package/src/components/waka-ad-sidebar/index.tsx +3 -2
  66. package/src/components/waka-ad-sticky-footer/WakaAdStickyFooter.stories.tsx +88 -0
  67. package/src/components/waka-address-autocomplete/WakaAddressAutocomplete.stories.tsx +46 -0
  68. package/src/components/waka-admincrumb/WakaAdmincrumb.stories.tsx +166 -0
  69. package/src/components/waka-alert-panel/WakaAlertPanel.stories.tsx +45 -0
  70. package/src/components/waka-alert-stack/WakaAlertStack.stories.tsx +62 -0
  71. package/src/components/waka-allocation-matrix/WakaAllocationMatrix.stories.tsx +68 -0
  72. package/src/components/waka-approval-chain/WakaApprovalChain.stories.tsx +63 -0
  73. package/src/components/waka-audit-log/WakaAuditLog.stories.tsx +73 -0
  74. package/src/components/waka-autocomplete/WakaAutocomplete.stories.tsx +132 -172
  75. package/src/components/waka-biometric-prompt/WakaBiometricPrompt.stories.tsx +48 -0
  76. package/src/components/waka-breadcrumb/WakaBreadcrumb.stories.tsx +74 -191
  77. package/src/components/waka-breadcrumb-path/WakaBreadcrumbPath.stories.tsx +40 -0
  78. package/src/components/waka-budget-burn/WakaBudgetBurn.stories.tsx +86 -0
  79. package/src/components/waka-capacity-planner/WakaCapacityPlanner.stories.tsx +273 -0
  80. package/src/components/waka-cart-summary/WakaCartSummary.stories.tsx +176 -0
  81. package/src/components/waka-cart-summary/index.tsx +19 -10
  82. package/src/components/waka-challenge-timer/WakaChallengeTimer.stories.tsx +98 -0
  83. package/src/components/waka-chat-bubble/WakaChatBubble.stories.tsx +118 -0
  84. package/src/components/waka-checklist/WakaChecklist.stories.tsx +71 -0
  85. package/src/components/waka-checkout-stepper/WakaCheckoutStepper.stories.tsx +102 -0
  86. package/src/components/waka-cohort-table/WakaCohortTable.stories.tsx +56 -0
  87. package/src/components/waka-color-picker/WakaColorPicker.stories.tsx +99 -155
  88. package/src/components/waka-combo-counter/WakaComboCounter.stories.tsx +128 -0
  89. package/src/components/waka-command-bar/WakaCommandBar.stories.tsx +45 -0
  90. package/src/components/waka-compare-period/WakaComparePeriod.stories.tsx +76 -0
  91. package/src/components/waka-config-comparator/WakaConfigComparator.stories.tsx +143 -0
  92. package/src/components/waka-connection-matrix/WakaConnectionMatrix.stories.tsx +52 -0
  93. package/src/components/waka-content-recommendation/WakaContentRecommendation.stories.tsx +41 -0
  94. package/src/components/waka-coupon-input/WakaCouponInput.stories.tsx +126 -0
  95. package/src/components/waka-credit-card-input/WakaCreditCardInput.stories.tsx +120 -0
  96. package/src/components/waka-datetime-picker.form-integration/WakaDateTimePickerForm.stories.tsx +79 -0
  97. package/src/components/waka-dependency-tree/WakaDependencyTree.stories.tsx +72 -0
  98. package/src/components/waka-device-trust/WakaDeviceTrust.stories.tsx +109 -0
  99. package/src/components/waka-empty-state/WakaEmptyState.stories.tsx +87 -0
  100. package/src/components/waka-feature-announcement/WakaFeatureAnnouncement.stories.tsx +47 -0
  101. package/src/components/waka-feature-flag-row/WakaFeatureFlagRow.stories.tsx +188 -0
  102. package/src/components/waka-file-upload/WakaFileUpload.stories.tsx +118 -174
  103. package/src/components/waka-floating-nav/WakaFloatingNav.stories.tsx +53 -0
  104. package/src/components/waka-goal-progress/WakaGoalProgress.stories.tsx +137 -0
  105. package/src/components/waka-hotspot/WakaHotspot.stories.tsx +56 -0
  106. package/src/components/waka-invoice-preview/WakaInvoicePreview.stories.tsx +169 -0
  107. package/src/components/waka-kpi-dashboard/WakaKpiDashboard.stories.tsx +46 -0
  108. package/src/components/waka-level-progress/WakaLevelProgress.stories.tsx +94 -75
  109. package/src/components/waka-liquid-button/WakaLiquidButton.stories.tsx +45 -0
  110. package/src/components/waka-magic-link/WakaMagicLink.stories.tsx +61 -0
  111. package/src/components/waka-magnetic-button/WakaMagneticButton.stories.tsx +40 -0
  112. package/src/components/waka-mention-input/WakaMentionInput.stories.tsx +140 -0
  113. package/src/components/waka-milestone-road/WakaMilestoneRoad.stories.tsx +143 -0
  114. package/src/components/waka-orbital-menu/WakaOrbitalMenu.stories.tsx +54 -0
  115. package/src/components/waka-order-tracker/WakaOrderTracker.stories.tsx +163 -0
  116. package/src/components/waka-outstream-video/WakaOutstreamVideo.stories.tsx +94 -0
  117. package/src/components/waka-pagination/WakaPagination.stories.tsx +110 -280
  118. package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +132 -268
  119. package/src/components/waka-payment-method-picker/WakaPaymentMethodPicker.stories.tsx +141 -0
  120. package/src/components/waka-permission-matrix/WakaPermissionMatrix.stories.tsx +124 -0
  121. package/src/components/waka-phone-input/WakaPhoneInput.stories.tsx +56 -0
  122. package/src/components/waka-points-popup/WakaPointsPopup.stories.tsx +96 -0
  123. package/src/components/waka-power-up/WakaPowerUp.stories.tsx +121 -0
  124. package/src/components/waka-presence-indicator/WakaPresenceIndicator.stories.tsx +49 -0
  125. package/src/components/waka-pricing-table/WakaPricingTable.stories.tsx +159 -0
  126. package/src/components/waka-product-card/WakaProductCard.stories.tsx +202 -0
  127. package/src/components/waka-progress-onboarding/WakaProgressOnboarding.stories.tsx +57 -0
  128. package/src/components/waka-pull-to-refresh/WakaPullToRefresh.stories.tsx +51 -0
  129. package/src/components/waka-rank-badge/WakaRankBadge.stories.tsx +108 -0
  130. package/src/components/waka-rating-input/WakaRatingInput.stories.tsx +51 -0
  131. package/src/components/waka-reaction-picker/WakaReactionPicker.stories.tsx +52 -0
  132. package/src/components/waka-region-map/WakaRegionMap.stories.tsx +181 -0
  133. package/src/components/waka-resource-pool/WakaResourcePool.stories.tsx +70 -0
  134. package/src/components/waka-rich-text-editor/WakaRichTextEditor.stories.tsx +108 -197
  135. package/src/components/waka-rollback-slider/WakaRollbackSlider.stories.tsx +41 -0
  136. package/src/components/waka-schedule-picker/WakaSchedulePicker.stories.tsx +64 -0
  137. package/src/components/waka-season-pass/WakaSeasonPass.stories.tsx +107 -0
  138. package/src/components/waka-security-scan-result/WakaSecurityScanResult.stories.tsx +146 -0
  139. package/src/components/waka-security-score/WakaSecurityScore.stories.tsx +63 -0
  140. package/src/components/waka-session-manager/WakaSessionManager.stories.tsx +68 -0
  141. package/src/components/waka-signature-pad/WakaSignaturePad.stories.tsx +159 -0
  142. package/src/components/waka-signature-pad/index.tsx +5 -3
  143. package/src/components/waka-sla-tracker/WakaSlaTracker.stories.tsx +65 -0
  144. package/src/components/waka-slider-range/WakaSliderRange.stories.tsx +66 -0
  145. package/src/components/waka-sponsored-badge/WakaSponsoredBadge.stories.tsx +60 -0
  146. package/src/components/waka-sponsored-card/WakaSponsoredCard.stories.tsx +64 -0
  147. package/src/components/waka-sponsored-feed/WakaSponsoredFeed.stories.tsx +58 -0
  148. package/src/components/waka-spotlight/WakaSpotlight.stories.tsx +53 -0
  149. package/src/components/waka-stats-hexagon/WakaStatsHexagon.stories.tsx +161 -0
  150. package/src/components/waka-stepper/WakaStepper.stories.tsx +137 -410
  151. package/src/components/waka-swipe-card/WakaSwipeCard.stories.tsx +51 -0
  152. package/src/components/waka-tag-input/WakaTagInput.stories.tsx +224 -0
  153. package/src/components/waka-team-banner/WakaTeamBanner.stories.tsx +50 -0
  154. package/src/components/waka-theme-creator/WakaThemeCreator.stories.tsx +58 -0
  155. package/src/components/waka-theme-manager/WakaThemeManager.stories.tsx +298 -0
  156. package/src/components/waka-theme-manager/index.tsx +6 -11
  157. package/src/components/waka-thread-view/WakaThreadView.stories.tsx +143 -0
  158. package/src/components/waka-timeline/WakaTimeline.stories.tsx +171 -324
  159. package/src/components/waka-tooltip-tour/WakaTooltipTour.stories.tsx +92 -0
  160. package/src/components/waka-tour-guide/WakaTourGuide.stories.tsx +89 -0
  161. package/src/components/waka-treemap-chart/WakaTreemapChart.stories.tsx +234 -129
  162. package/src/components/waka-treemap-chart/index.tsx +2 -2
  163. package/src/components/waka-two-factor-setup/WakaTwoFactorSetup.stories.tsx +142 -0
  164. package/src/components/waka-typing-indicator/WakaTypingIndicator.stories.tsx +134 -0
  165. package/src/components/waka-video-ad/WakaVideoAd.stories.tsx +138 -0
  166. package/src/components/waka-video-call/WakaVideoCall.stories.tsx +186 -0
  167. package/src/components/waka-video-overlay/WakaVideoOverlay.stories.tsx +100 -0
  168. package/src/components/waka-voice-message/WakaVoiceMessage.stories.tsx +190 -0
  169. package/src/components/waka-welcome-modal/WakaWelcomeModal.stories.tsx +87 -0
  170. package/src/components/waka-xp-bar/WakaXPBar.stories.tsx +29 -29
  171. package/dist/useDataTableImport-D8R2HQl6.mjs +0 -229
  172. package/dist/useDataTableImport-S_hhA5Wo.js +0 -9
  173. package/src/components/DataTable/README.md +0 -446
@@ -0,0 +1,470 @@
1
+ "use client"
2
+
3
+ import { Badge } from "../../components/badge"
4
+ import { Button } from "../../components/button"
5
+ import { Card } from "../../components/card"
6
+ import { Progress } from "../../components/progress"
7
+ import { ScrollArea } from "../../components/scroll-area"
8
+ import {
9
+ Database,
10
+ RefreshCw,
11
+ Calendar,
12
+ FileText,
13
+ HardDrive,
14
+ Clock,
15
+ Download,
16
+ CheckCircle2,
17
+ XCircle,
18
+ Loader2,
19
+ AlertCircle,
20
+ Globe,
21
+ } from "lucide-react"
22
+ import { cn } from "../../utils/cn"
23
+
24
+ export type UpdateStatus = "success" | "failed" | "in_progress" | "scheduled"
25
+
26
+ export interface SignatureDatabase {
27
+ name: string
28
+ version: string
29
+ signatureCount: number
30
+ size: number
31
+ lastUpdated: Date
32
+ buildTime: Date
33
+ }
34
+
35
+ export interface SignatureUpdate {
36
+ id: string
37
+ date: Date
38
+ fromVersion: string
39
+ toVersion: string
40
+ newSignatures: number
41
+ removedSignatures: number
42
+ duration: number
43
+ status: UpdateStatus
44
+ source: string
45
+ }
46
+
47
+ export interface FreshclamConfig {
48
+ updateFrequency: number // hours
49
+ mirror: string
50
+ lastCheck: Date
51
+ checksToday: number
52
+ proxyEnabled: boolean
53
+ }
54
+
55
+ export interface SignatureDatabaseManagerProps {
56
+ databases?: SignatureDatabase[]
57
+ updates?: SignatureUpdate[]
58
+ freshclamConfig?: FreshclamConfig
59
+ totalSignatures?: number
60
+ isUpdating?: boolean
61
+ onUpdateNow?: () => void
62
+ onScheduleUpdate?: (frequency: number) => void
63
+ onViewChangelog?: (updateId: string) => void
64
+ className?: string
65
+ }
66
+
67
+ const defaultDatabases: SignatureDatabase[] = [
68
+ {
69
+ name: "main.cvd",
70
+ version: "62",
71
+ signatureCount: 6647427,
72
+ size: 162.5 * 1024 * 1024,
73
+ lastUpdated: new Date("2024-12-15T10:30:00"),
74
+ buildTime: new Date("2024-12-15T08:00:00"),
75
+ },
76
+ {
77
+ name: "daily.cvd",
78
+ version: "27458",
79
+ signatureCount: 2145789,
80
+ size: 89.3 * 1024 * 1024,
81
+ lastUpdated: new Date("2025-01-15T14:20:00"),
82
+ buildTime: new Date("2025-01-15T12:00:00"),
83
+ },
84
+ {
85
+ name: "bytecode.cvd",
86
+ version: "334",
87
+ signatureCount: 52438,
88
+ size: 2.8 * 1024 * 1024,
89
+ lastUpdated: new Date("2025-01-10T09:15:00"),
90
+ buildTime: new Date("2025-01-10T06:00:00"),
91
+ },
92
+ ]
93
+
94
+ const defaultUpdates: SignatureUpdate[] = [
95
+ {
96
+ id: "upd-1",
97
+ date: new Date("2025-01-15T14:20:00"),
98
+ fromVersion: "27457",
99
+ toVersion: "27458",
100
+ newSignatures: 1247,
101
+ removedSignatures: 89,
102
+ duration: 45,
103
+ status: "success",
104
+ source: "daily.cvd",
105
+ },
106
+ {
107
+ id: "upd-2",
108
+ date: new Date("2025-01-14T14:15:00"),
109
+ fromVersion: "27456",
110
+ toVersion: "27457",
111
+ newSignatures: 2134,
112
+ removedSignatures: 156,
113
+ duration: 52,
114
+ status: "success",
115
+ source: "daily.cvd",
116
+ },
117
+ {
118
+ id: "upd-3",
119
+ date: new Date("2025-01-13T14:10:00"),
120
+ fromVersion: "27455",
121
+ toVersion: "27456",
122
+ newSignatures: 1876,
123
+ removedSignatures: 203,
124
+ duration: 48,
125
+ status: "success",
126
+ source: "daily.cvd",
127
+ },
128
+ {
129
+ id: "upd-4",
130
+ date: new Date("2025-01-12T14:05:00"),
131
+ fromVersion: "27454",
132
+ toVersion: "27455",
133
+ newSignatures: 0,
134
+ removedSignatures: 0,
135
+ duration: 12,
136
+ status: "failed",
137
+ source: "daily.cvd",
138
+ },
139
+ {
140
+ id: "upd-5",
141
+ date: new Date("2025-01-11T14:00:00"),
142
+ fromVersion: "27453",
143
+ toVersion: "27454",
144
+ newSignatures: 3245,
145
+ removedSignatures: 178,
146
+ duration: 67,
147
+ status: "success",
148
+ source: "daily.cvd",
149
+ },
150
+ ]
151
+
152
+ const defaultFreshclamConfig: FreshclamConfig = {
153
+ updateFrequency: 24,
154
+ mirror: "database.clamav.net",
155
+ lastCheck: new Date("2025-01-15T14:20:00"),
156
+ checksToday: 12,
157
+ proxyEnabled: false,
158
+ }
159
+
160
+ const formatBytes = (bytes: number): string => {
161
+ if (bytes < 1024) return bytes + " B"
162
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + " KB"
163
+ return (bytes / (1024 * 1024)).toFixed(2) + " MB"
164
+ }
165
+
166
+ const formatDate = (date: Date): string => {
167
+ return new Intl.DateTimeFormat("fr-FR", {
168
+ year: "numeric",
169
+ month: "short",
170
+ day: "numeric",
171
+ hour: "2-digit",
172
+ minute: "2-digit",
173
+ }).format(date)
174
+ }
175
+
176
+ const formatNumber = (num: number): string => {
177
+ return new Intl.NumberFormat("fr-FR").format(num)
178
+ }
179
+
180
+ const getStatusIcon = (status: UpdateStatus) => {
181
+ switch (status) {
182
+ case "success":
183
+ return <CheckCircle2 className="size-4 text-green-600 dark:text-green-400" />
184
+ case "failed":
185
+ return <XCircle className="size-4 text-red-600 dark:text-red-400" />
186
+ case "in_progress":
187
+ return <Loader2 className="size-4 text-blue-600 dark:text-blue-400 animate-spin" />
188
+ case "scheduled":
189
+ return <Clock className="size-4 text-amber-600 dark:text-amber-400" />
190
+ }
191
+ }
192
+
193
+ const getStatusBadge = (status: UpdateStatus) => {
194
+ switch (status) {
195
+ case "success":
196
+ return <Badge variant="secondary" className="bg-green-500/10 text-green-600 border-green-500/20">Réussie</Badge>
197
+ case "failed":
198
+ return <Badge variant="destructive">Échouée</Badge>
199
+ case "in_progress":
200
+ return <Badge variant="default">En cours</Badge>
201
+ case "scheduled":
202
+ return <Badge variant="secondary" className="bg-yellow-500/10 text-yellow-600 border-yellow-500/20">Planifiée</Badge>
203
+ }
204
+ }
205
+
206
+ export function SignatureDatabaseManager({
207
+ databases = defaultDatabases,
208
+ updates = defaultUpdates,
209
+ freshclamConfig = defaultFreshclamConfig,
210
+ totalSignatures = databases.reduce((sum, db) => sum + db.signatureCount, 0),
211
+ isUpdating = false,
212
+ onUpdateNow,
213
+ onScheduleUpdate,
214
+ onViewChangelog,
215
+ className,
216
+ }: SignatureDatabaseManagerProps) {
217
+ const latestUpdate = updates[0]
218
+
219
+ return (
220
+ <div className={cn("space-y-6", className)}>
221
+ {/* Status actuel */}
222
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
223
+ <Card className="p-6">
224
+ <div className="flex items-center justify-between">
225
+ <div>
226
+ <p className="text-sm font-medium text-muted-foreground">Version DB</p>
227
+ <h3 className="text-2xl font-bold">{latestUpdate?.toVersion || "N/A"}</h3>
228
+ </div>
229
+ <Database className="size-8 text-blue-600 dark:text-blue-400" />
230
+ </div>
231
+ </Card>
232
+
233
+ <Card className="p-6">
234
+ <div className="flex items-center justify-between">
235
+ <div>
236
+ <p className="text-sm font-medium text-muted-foreground">Total Signatures</p>
237
+ <h3 className="text-2xl font-bold">{formatNumber(totalSignatures)}</h3>
238
+ </div>
239
+ <FileText className="size-8 text-purple-600 dark:text-purple-400" />
240
+ </div>
241
+ </Card>
242
+
243
+ <Card className="p-6">
244
+ <div className="flex items-center justify-between">
245
+ <div>
246
+ <p className="text-sm font-medium text-muted-foreground">Dernière MAJ</p>
247
+ <h3 className="text-sm font-bold">
248
+ {latestUpdate ? formatDate(latestUpdate.date) : "N/A"}
249
+ </h3>
250
+ </div>
251
+ <Clock className="size-8 text-green-600 dark:text-green-400" />
252
+ </div>
253
+ </Card>
254
+
255
+ <Card className="p-6">
256
+ <div className="flex items-center justify-between">
257
+ <div>
258
+ <p className="text-sm font-medium text-muted-foreground">Taille Totale</p>
259
+ <h3 className="text-2xl font-bold">
260
+ {formatBytes(databases.reduce((sum, db) => sum + db.size, 0))}
261
+ </h3>
262
+ </div>
263
+ <HardDrive className="size-8 text-amber-600 dark:text-amber-400" />
264
+ </div>
265
+ </Card>
266
+ </div>
267
+
268
+ <div className="grid gap-6 lg:grid-cols-2">
269
+ {/* Bases de données */}
270
+ <Card className="p-6">
271
+ <div className="mb-4 flex items-center justify-between">
272
+ <h3 className="text-lg font-semibold">Bases de données</h3>
273
+ <Badge variant="outline">{databases.length} bases</Badge>
274
+ </div>
275
+
276
+ <div className="space-y-4">
277
+ {databases.map((db) => (
278
+ <div
279
+ key={db.name}
280
+ className="rounded-lg border border-border bg-muted/50 p-4 space-y-2"
281
+ >
282
+ <div className="flex items-center justify-between">
283
+ <div className="flex items-center gap-2">
284
+ <Database className="size-4 text-muted-foreground" />
285
+ <span className="font-medium">{db.name}</span>
286
+ </div>
287
+ <Badge variant="secondary">v{db.version}</Badge>
288
+ </div>
289
+
290
+ <div className="grid grid-cols-2 gap-2 text-sm">
291
+ <div>
292
+ <p className="text-muted-foreground">Signatures</p>
293
+ <p className="font-medium">{formatNumber(db.signatureCount)}</p>
294
+ </div>
295
+ <div>
296
+ <p className="text-muted-foreground">Taille</p>
297
+ <p className="font-medium">{formatBytes(db.size)}</p>
298
+ </div>
299
+ <div className="col-span-2">
300
+ <p className="text-muted-foreground">Dernière mise à jour</p>
301
+ <p className="font-medium">{formatDate(db.lastUpdated)}</p>
302
+ </div>
303
+ </div>
304
+ </div>
305
+ ))}
306
+ </div>
307
+ </Card>
308
+
309
+ {/* Freshclam Config */}
310
+ <Card className="p-6">
311
+ <div className="mb-4 flex items-center justify-between">
312
+ <h3 className="text-lg font-semibold">Configuration Freshclam</h3>
313
+ <Button
314
+ size="sm"
315
+ variant="outline"
316
+ onClick={() => onScheduleUpdate?.(freshclamConfig.updateFrequency)}
317
+ >
318
+ <Calendar className="mr-2 size-4" />
319
+ Planifier
320
+ </Button>
321
+ </div>
322
+
323
+ <div className="space-y-4">
324
+ <div className="flex items-center justify-between rounded-lg border border-border bg-muted/50 p-4">
325
+ <div>
326
+ <p className="text-sm text-muted-foreground">Fréquence de MAJ</p>
327
+ <p className="text-lg font-semibold">
328
+ Toutes les {freshclamConfig.updateFrequency}h
329
+ </p>
330
+ </div>
331
+ <Clock className="size-6 text-blue-600 dark:text-blue-400" />
332
+ </div>
333
+
334
+ <div className="flex items-center justify-between rounded-lg border border-border bg-muted/50 p-4">
335
+ <div>
336
+ <p className="text-sm text-muted-foreground">Mirror utilisé</p>
337
+ <p className="text-lg font-semibold">{freshclamConfig.mirror}</p>
338
+ </div>
339
+ <Globe className="size-6 text-purple-600 dark:text-purple-400" />
340
+ </div>
341
+
342
+ <div className="flex items-center justify-between rounded-lg border border-border bg-muted/50 p-4">
343
+ <div>
344
+ <p className="text-sm text-muted-foreground">Dernier check</p>
345
+ <p className="text-lg font-semibold">{formatDate(freshclamConfig.lastCheck)}</p>
346
+ </div>
347
+ <RefreshCw className="size-6 text-green-600 dark:text-green-400" />
348
+ </div>
349
+
350
+ <div className="flex items-center justify-between rounded-lg border border-border bg-muted/50 p-4">
351
+ <div>
352
+ <p className="text-sm text-muted-foreground">Checks aujourd'hui</p>
353
+ <p className="text-lg font-semibold">{freshclamConfig.checksToday}</p>
354
+ </div>
355
+ <AlertCircle className="size-6 text-amber-600 dark:text-amber-400" />
356
+ </div>
357
+
358
+ {freshclamConfig.proxyEnabled && (
359
+ <div className="rounded-lg border border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-950/50 p-3">
360
+ <div className="flex items-center gap-2 text-sm text-amber-800 dark:text-amber-200">
361
+ <AlertCircle className="size-4" />
362
+ <span>Proxy activé</span>
363
+ </div>
364
+ </div>
365
+ )}
366
+
367
+ <Button
368
+ className="w-full"
369
+ onClick={onUpdateNow}
370
+ disabled={isUpdating}
371
+ >
372
+ {isUpdating ? (
373
+ <>
374
+ <Loader2 className="mr-2 size-4 animate-spin" />
375
+ Mise à jour en cours...
376
+ </>
377
+ ) : (
378
+ <>
379
+ <Download className="mr-2 size-4" />
380
+ Mettre à jour maintenant
381
+ </>
382
+ )}
383
+ </Button>
384
+ </div>
385
+ </Card>
386
+ </div>
387
+
388
+ {/* Historique des MAJ */}
389
+ <Card className="p-6">
390
+ <div className="mb-4 flex items-center justify-between">
391
+ <h3 className="text-lg font-semibold">Historique des mises à jour</h3>
392
+ <Badge variant="outline">{updates.length} mises à jour</Badge>
393
+ </div>
394
+
395
+ <ScrollArea className="h-[400px]">
396
+ <div className="space-y-3">
397
+ {updates.map((update, index) => (
398
+ <div
399
+ key={update.id}
400
+ className="relative rounded-lg border border-border bg-muted/50 p-4"
401
+ >
402
+ {index < updates.length - 1 && (
403
+ <div className="absolute left-6 top-12 h-full w-px bg-border" />
404
+ )}
405
+
406
+ <div className="flex items-start gap-3">
407
+ <div className="flex size-8 shrink-0 items-center justify-center rounded-full border border-border bg-background">
408
+ {getStatusIcon(update.status)}
409
+ </div>
410
+
411
+ <div className="flex-1 space-y-2">
412
+ <div className="flex items-center justify-between">
413
+ <div>
414
+ <p className="font-medium">
415
+ {update.source} - v{update.fromVersion} → v{update.toVersion}
416
+ </p>
417
+ <p className="text-sm text-muted-foreground">
418
+ {formatDate(update.date)}
419
+ </p>
420
+ </div>
421
+ {getStatusBadge(update.status)}
422
+ </div>
423
+
424
+ {update.status === "success" && (
425
+ <div className="grid grid-cols-3 gap-3 text-sm">
426
+ <div>
427
+ <p className="text-muted-foreground">Ajoutées</p>
428
+ <p className="font-medium text-green-600 dark:text-green-400">
429
+ +{formatNumber(update.newSignatures)}
430
+ </p>
431
+ </div>
432
+ <div>
433
+ <p className="text-muted-foreground">Supprimées</p>
434
+ <p className="font-medium text-red-600 dark:text-red-400">
435
+ -{formatNumber(update.removedSignatures)}
436
+ </p>
437
+ </div>
438
+ <div>
439
+ <p className="text-muted-foreground">Durée</p>
440
+ <p className="font-medium">{update.duration}s</p>
441
+ </div>
442
+ </div>
443
+ )}
444
+
445
+ {update.status === "failed" && (
446
+ <div className="rounded-lg border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950/50 p-2">
447
+ <p className="text-sm text-red-800 dark:text-red-200">
448
+ Échec de la mise à jour
449
+ </p>
450
+ </div>
451
+ )}
452
+
453
+ <Button
454
+ size="sm"
455
+ variant="ghost"
456
+ onClick={() => onViewChangelog?.(update.id)}
457
+ >
458
+ <FileText className="mr-2 size-3" />
459
+ Voir le changelog
460
+ </Button>
461
+ </div>
462
+ </div>
463
+ </div>
464
+ ))}
465
+ </div>
466
+ </ScrollArea>
467
+ </Card>
468
+ </div>
469
+ )
470
+ }
@@ -8,8 +8,22 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from ".
8
8
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../components/collapsible"
9
9
  import { ScrollArea } from "../../components/scroll-area"
10
10
  import { useToast } from "../../hooks/useToast"
11
- import { useTheme, ShadcnRegistryItem } from "../../context/theme-provider"
11
+ import { useTheme, type ShadcnRegistryItem } from "../../context/theme-provider"
12
12
  import { Login, type LoginConfig } from "../login"
13
+
14
+ // Hook sécurisé pour gérer le contexte de thème (optionnel)
15
+ function useThemeSafe() {
16
+ try {
17
+ return useTheme()
18
+ } catch {
19
+ // Le contexte n'est pas disponible, retourner des valeurs par défaut
20
+ return {
21
+ loadThemeFromJSON: async () => {},
22
+ isDarkMode: false,
23
+ toggleDarkMode: () => {},
24
+ }
25
+ }
26
+ }
13
27
  import { WakaDashboard, defaultDashboardStats } from "../dashboard"
14
28
  import { WakaChat, defaultChatUser, defaultChatConversations, defaultChatMessages } from "../chat"
15
29
  import {
@@ -513,7 +527,7 @@ export function WakaThemeCreatorBlock({
513
527
  className,
514
528
  }: WakaThemeCreatorBlockProps) {
515
529
  const { toast } = useToast()
516
- const { loadThemeFromJSON, isDarkMode, toggleDarkMode } = useTheme()
530
+ const { loadThemeFromJSON, isDarkMode, toggleDarkMode } = useThemeSafe()
517
531
 
518
532
  // Theme state
519
533
  const [currentTheme, setCurrentTheme] = React.useState<ThemeCreatorBlockTheme>(
@@ -0,0 +1,152 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+ import { ThreatAlertBanner, ThreatAlert } from "./index"
3
+
4
+ const meta = {
5
+ title: "Blocks/Antivirus/ThreatAlertBanner",
6
+ component: ThreatAlertBanner,
7
+ parameters: {
8
+ layout: "padded",
9
+ },
10
+ tags: ["autodocs"],
11
+ } satisfies Meta<typeof ThreatAlertBanner>
12
+
13
+ export default meta
14
+ type Story = StoryObj<typeof meta>
15
+
16
+ const criticalAlert: ThreatAlert = {
17
+ id: "1",
18
+ threatName: "Ransomware.WannaCry.Variant",
19
+ filename: "important_document.pdf.exe",
20
+ filePath: "/home/user/Documents/important_document.pdf.exe",
21
+ severity: "critical",
22
+ action: "quarantined",
23
+ detectedAt: new Date(Date.now() - 2 * 60000),
24
+ userId: "john.doe@company.com",
25
+ engineVersion: "ClamAV 1.4.1 / 2024-02-12",
26
+ dismissed: false,
27
+ }
28
+
29
+ const highAlert: ThreatAlert = {
30
+ id: "2",
31
+ threatName: "Trojan.GenericKD.87654321",
32
+ filename: "update.exe",
33
+ filePath: "/var/www/uploads/suspicious/update.exe",
34
+ severity: "high",
35
+ action: "blocked",
36
+ detectedAt: new Date(Date.now() - 10 * 60000),
37
+ userId: "admin@company.com",
38
+ engineVersion: "ClamAV 1.4.1 / 2024-02-12",
39
+ dismissed: false,
40
+ }
41
+
42
+ const mediumAlert: ThreatAlert = {
43
+ id: "3",
44
+ threatName: "PUA.Win.Adware.BundleInstaller",
45
+ filename: "freeware_installer.exe",
46
+ filePath: "/tmp/downloads/freeware_installer.exe",
47
+ severity: "medium",
48
+ action: "deleted",
49
+ detectedAt: new Date(Date.now() - 30 * 60000),
50
+ userId: "user@company.com",
51
+ engineVersion: "ClamAV 1.4.1 / 2024-02-12",
52
+ dismissed: false,
53
+ }
54
+
55
+ const lowAlert: ThreatAlert = {
56
+ id: "4",
57
+ threatName: "Test.EICAR.Signature",
58
+ filename: "eicar.com",
59
+ filePath: "/home/user/test/eicar.com",
60
+ severity: "low",
61
+ action: "allowed",
62
+ detectedAt: new Date(Date.now() - 60 * 60000),
63
+ userId: "security-team@company.com",
64
+ engineVersion: "ClamAV 1.4.1 / 2024-02-12",
65
+ dismissed: false,
66
+ }
67
+
68
+ export const Critical: Story = {
69
+ args: {
70
+ alerts: [criticalAlert],
71
+ onDismiss: (alertId: string) => {
72
+ console.log("Dismiss alert:", alertId)
73
+ },
74
+ onViewDetails: (alertId: string) => {
75
+ console.log("View details:", alertId)
76
+ },
77
+ onViewQuarantine: () => {
78
+ console.log("View quarantine")
79
+ },
80
+ },
81
+ }
82
+
83
+ export const MultipleAlerts: Story = {
84
+ args: {
85
+ alerts: [criticalAlert, highAlert, mediumAlert, lowAlert],
86
+ maxVisible: 3,
87
+ onDismiss: (alertId: string) => {
88
+ console.log("Dismiss alert:", alertId)
89
+ },
90
+ onDismissAll: () => {
91
+ console.log("Dismiss all alerts")
92
+ },
93
+ onViewDetails: (alertId: string) => {
94
+ console.log("View details:", alertId)
95
+ },
96
+ onViewQuarantine: () => {
97
+ console.log("View quarantine")
98
+ },
99
+ },
100
+ }
101
+
102
+ export const LowSeverity: Story = {
103
+ args: {
104
+ alerts: [lowAlert],
105
+ onDismiss: (alertId: string) => {
106
+ console.log("Dismiss alert:", alertId)
107
+ },
108
+ onViewDetails: (alertId: string) => {
109
+ console.log("View details:", alertId)
110
+ },
111
+ },
112
+ }
113
+
114
+ export const AllDismissed: Story = {
115
+ args: {
116
+ alerts: [
117
+ { ...criticalAlert, dismissed: true },
118
+ { ...highAlert, dismissed: true },
119
+ { ...mediumAlert, dismissed: true },
120
+ { ...lowAlert, dismissed: true },
121
+ ],
122
+ onDismiss: (alertId: string) => {
123
+ console.log("Dismiss alert:", alertId)
124
+ },
125
+ },
126
+ }
127
+
128
+ export const ManyAlerts: Story = {
129
+ args: {
130
+ alerts: [
131
+ criticalAlert,
132
+ { ...highAlert, id: "5", detectedAt: new Date(Date.now() - 5 * 60000) },
133
+ { ...highAlert, id: "6", filename: "malware.zip", detectedAt: new Date(Date.now() - 8 * 60000) },
134
+ mediumAlert,
135
+ { ...mediumAlert, id: "7", filename: "suspicious.js", detectedAt: new Date(Date.now() - 45 * 60000) },
136
+ lowAlert,
137
+ ],
138
+ maxVisible: 3,
139
+ onDismiss: (alertId: string) => {
140
+ console.log("Dismiss alert:", alertId)
141
+ },
142
+ onDismissAll: () => {
143
+ console.log("Dismiss all alerts")
144
+ },
145
+ onViewDetails: (alertId: string) => {
146
+ console.log("View details:", alertId)
147
+ },
148
+ onViewQuarantine: () => {
149
+ console.log("View quarantine")
150
+ },
151
+ },
152
+ }