@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,418 @@
1
+ "use client"
2
+
3
+ import { useState } from "react"
4
+ import { Shield, Plus, X, RotateCcw } from "lucide-react"
5
+ import { Badge } from "../../components/badge"
6
+ import { Button } from "../../components/button"
7
+ import { Card } from "../../components/card"
8
+ import { Input } from "../../components/input"
9
+ import { Label } from "../../components/label"
10
+ import { Switch } from "../../components/switch"
11
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../components/select"
12
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from "../../components/tabs"
13
+ import { cn } from "../../utils/cn"
14
+
15
+ export type DetectionAction = "quarantine" | "delete" | "notify_only" | "block_and_notify"
16
+
17
+ export interface ScanPolicy {
18
+ autoScanOnUpload: boolean
19
+ maxFileSize: number // bytes
20
+ maxFileSizeUnit: "MB" | "GB"
21
+ scanTimeout: number // seconds
22
+ maxConcurrentScans: number
23
+ scanArchives: boolean
24
+ maxArchiveDepth: number
25
+ excludedExtensions: string[]
26
+ excludedPaths: string[]
27
+ whitelistedHashes: string[]
28
+ detectionAction: DetectionAction
29
+ notifyAdmin: boolean
30
+ notifyUser: boolean
31
+ auditLog: boolean
32
+ }
33
+
34
+ export interface ScanPolicyEditorProps {
35
+ policy?: ScanPolicy
36
+ isLoading?: boolean
37
+ onSave?: (policy: ScanPolicy) => void
38
+ onCancel?: () => void
39
+ onResetDefaults?: () => void
40
+ className?: string
41
+ }
42
+
43
+ export const defaultScanPolicy: ScanPolicy = {
44
+ autoScanOnUpload: true,
45
+ maxFileSize: 100,
46
+ maxFileSizeUnit: "MB",
47
+ scanTimeout: 120,
48
+ maxConcurrentScans: 4,
49
+ scanArchives: true,
50
+ maxArchiveDepth: 3,
51
+ excludedExtensions: [".log", ".tmp", ".lock"],
52
+ excludedPaths: ["/tmp", "/var/cache"],
53
+ whitelistedHashes: [],
54
+ detectionAction: "quarantine",
55
+ notifyAdmin: true,
56
+ notifyUser: true,
57
+ auditLog: true,
58
+ }
59
+
60
+ const detectionActionOptions = [
61
+ { value: "quarantine", label: "Quarantaine" },
62
+ { value: "delete", label: "Suppression" },
63
+ { value: "notify_only", label: "Notification seule" },
64
+ { value: "block_and_notify", label: "Bloquer + notifier" },
65
+ ]
66
+
67
+ const fileSizeUnitOptions = [
68
+ { value: "MB", label: "MB" },
69
+ { value: "GB", label: "GB" },
70
+ ]
71
+
72
+ export function ScanPolicyEditor({
73
+ policy = defaultScanPolicy,
74
+ isLoading = false,
75
+ onSave,
76
+ onCancel,
77
+ onResetDefaults,
78
+ className,
79
+ }: ScanPolicyEditorProps) {
80
+ const [localPolicy, setLocalPolicy] = useState<ScanPolicy>(policy)
81
+ const [newExtension, setNewExtension] = useState("")
82
+ const [newPath, setNewPath] = useState("")
83
+ const [newHash, setNewHash] = useState("")
84
+
85
+ const updatePolicy = (updates: Partial<ScanPolicy>) => {
86
+ setLocalPolicy((prev) => ({ ...prev, ...updates }))
87
+ }
88
+
89
+ const addExtension = () => {
90
+ if (newExtension.trim() && !localPolicy.excludedExtensions.includes(newExtension.trim())) {
91
+ updatePolicy({
92
+ excludedExtensions: [...localPolicy.excludedExtensions, newExtension.trim()],
93
+ })
94
+ setNewExtension("")
95
+ }
96
+ }
97
+
98
+ const removeExtension = (ext: string) => {
99
+ updatePolicy({
100
+ excludedExtensions: localPolicy.excludedExtensions.filter((e) => e !== ext),
101
+ })
102
+ }
103
+
104
+ const addPath = () => {
105
+ if (newPath.trim() && !localPolicy.excludedPaths.includes(newPath.trim())) {
106
+ updatePolicy({
107
+ excludedPaths: [...localPolicy.excludedPaths, newPath.trim()],
108
+ })
109
+ setNewPath("")
110
+ }
111
+ }
112
+
113
+ const removePath = (path: string) => {
114
+ updatePolicy({
115
+ excludedPaths: localPolicy.excludedPaths.filter((p) => p !== path),
116
+ })
117
+ }
118
+
119
+ const addHash = () => {
120
+ if (newHash.trim() && !localPolicy.whitelistedHashes.includes(newHash.trim())) {
121
+ updatePolicy({
122
+ whitelistedHashes: [...localPolicy.whitelistedHashes, newHash.trim()],
123
+ })
124
+ setNewHash("")
125
+ }
126
+ }
127
+
128
+ const removeHash = (hash: string) => {
129
+ updatePolicy({
130
+ whitelistedHashes: localPolicy.whitelistedHashes.filter((h) => h !== hash),
131
+ })
132
+ }
133
+
134
+ const handleSave = () => {
135
+ onSave?.(localPolicy)
136
+ }
137
+
138
+ const handleResetDefaults = () => {
139
+ setLocalPolicy(defaultScanPolicy)
140
+ onResetDefaults?.()
141
+ }
142
+
143
+ return (
144
+ <Card className={cn("w-full", className)}>
145
+ <div className="flex items-center gap-3 p-6 border-b border-border/50">
146
+ <Shield className="w-5 h-5 text-primary" />
147
+ <div>
148
+ <h3 className="font-semibold text-foreground">Configuration des politiques de scan</h3>
149
+ <p className="text-sm text-muted-foreground">Definissez les regles de scan antivirus</p>
150
+ </div>
151
+ </div>
152
+
153
+ <Tabs defaultValue="general" className="w-full">
154
+ <TabsList className="w-full justify-start border-b border-border/50 rounded-none px-6">
155
+ <TabsTrigger value="general">Regles generales</TabsTrigger>
156
+ <TabsTrigger value="exclusions">Exclusions</TabsTrigger>
157
+ <TabsTrigger value="actions">Actions</TabsTrigger>
158
+ <TabsTrigger value="notifications">Notifications</TabsTrigger>
159
+ </TabsList>
160
+
161
+ <TabsContent value="general" className="p-6 space-y-6">
162
+ <div className="flex items-center justify-between">
163
+ <div className="space-y-0.5">
164
+ <Label>Scan automatique a l'upload</Label>
165
+ <p className="text-sm text-muted-foreground">Scanner tous les fichiers lors de l'envoi</p>
166
+ </div>
167
+ <Switch
168
+ checked={localPolicy.autoScanOnUpload}
169
+ onCheckedChange={(checked) => updatePolicy({ autoScanOnUpload: checked })}
170
+ disabled={isLoading}
171
+ />
172
+ </div>
173
+
174
+ <div className="grid grid-cols-2 gap-4">
175
+ <div className="space-y-2">
176
+ <Label>Taille maximale de fichier</Label>
177
+ <div className="flex gap-2">
178
+ <Input
179
+ type="number"
180
+ value={localPolicy.maxFileSize}
181
+ onChange={(e) => updatePolicy({ maxFileSize: parseInt(e.target.value) || 0 })}
182
+ disabled={isLoading}
183
+ className="flex-1"
184
+ />
185
+ <Select
186
+ value={localPolicy.maxFileSizeUnit}
187
+ onValueChange={(value) => updatePolicy({ maxFileSizeUnit: value as "MB" | "GB" })}
188
+ disabled={isLoading}
189
+ >
190
+ <SelectTrigger className="w-24">
191
+ <SelectValue />
192
+ </SelectTrigger>
193
+ <SelectContent>
194
+ {fileSizeUnitOptions.map((opt) => (
195
+ <SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>
196
+ ))}
197
+ </SelectContent>
198
+ </Select>
199
+ </div>
200
+ </div>
201
+
202
+ <div className="space-y-2">
203
+ <Label>Timeout de scan (secondes)</Label>
204
+ <Input
205
+ type="number"
206
+ value={localPolicy.scanTimeout}
207
+ onChange={(e) => updatePolicy({ scanTimeout: parseInt(e.target.value) || 0 })}
208
+ disabled={isLoading}
209
+ />
210
+ </div>
211
+ </div>
212
+
213
+ <div className="space-y-2">
214
+ <Label>Nombre maximum de scans concurrents</Label>
215
+ <Input
216
+ type="number"
217
+ value={localPolicy.maxConcurrentScans}
218
+ onChange={(e) => updatePolicy({ maxConcurrentScans: parseInt(e.target.value) || 1 })}
219
+ disabled={isLoading}
220
+ />
221
+ </div>
222
+
223
+ <div className="flex items-center justify-between">
224
+ <div className="space-y-0.5">
225
+ <Label>Scanner les archives (zip, tar, 7z)</Label>
226
+ <p className="text-sm text-muted-foreground">Analyser le contenu des fichiers archives</p>
227
+ </div>
228
+ <Switch
229
+ checked={localPolicy.scanArchives}
230
+ onCheckedChange={(checked) => updatePolicy({ scanArchives: checked })}
231
+ disabled={isLoading}
232
+ />
233
+ </div>
234
+
235
+ {localPolicy.scanArchives && (
236
+ <div className="space-y-2 pl-6">
237
+ <Label>Profondeur maximale d'archives imbriquees</Label>
238
+ <Input
239
+ type="number"
240
+ value={localPolicy.maxArchiveDepth}
241
+ onChange={(e) => updatePolicy({ maxArchiveDepth: parseInt(e.target.value) || 1 })}
242
+ disabled={isLoading}
243
+ />
244
+ </div>
245
+ )}
246
+ </TabsContent>
247
+
248
+ <TabsContent value="exclusions" className="p-6 space-y-6">
249
+ <div className="space-y-3">
250
+ <Label>Extensions exclues</Label>
251
+ <div className="flex gap-2">
252
+ <Input
253
+ placeholder=".ext"
254
+ value={newExtension}
255
+ onChange={(e) => setNewExtension(e.target.value)}
256
+ onKeyDown={(e) => e.key === "Enter" && addExtension()}
257
+ disabled={isLoading}
258
+ className="flex-1"
259
+ />
260
+ <Button onClick={addExtension} disabled={isLoading} size="sm">
261
+ <Plus className="w-4 h-4" />
262
+ </Button>
263
+ </div>
264
+ <div className="flex flex-wrap gap-2">
265
+ {localPolicy.excludedExtensions.map((ext) => (
266
+ <Badge key={ext} variant="secondary" className="flex items-center gap-1">
267
+ {ext}
268
+ <button
269
+ onClick={() => removeExtension(ext)}
270
+ disabled={isLoading}
271
+ className="ml-1 hover:text-destructive"
272
+ >
273
+ <X className="w-3 h-3" />
274
+ </button>
275
+ </Badge>
276
+ ))}
277
+ </div>
278
+ </div>
279
+
280
+ <div className="space-y-3">
281
+ <Label>Chemins exclus</Label>
282
+ <div className="flex gap-2">
283
+ <Input
284
+ placeholder="/path/to/exclude"
285
+ value={newPath}
286
+ onChange={(e) => setNewPath(e.target.value)}
287
+ onKeyDown={(e) => e.key === "Enter" && addPath()}
288
+ disabled={isLoading}
289
+ className="flex-1"
290
+ />
291
+ <Button onClick={addPath} disabled={isLoading} size="sm">
292
+ <Plus className="w-4 h-4" />
293
+ </Button>
294
+ </div>
295
+ <div className="flex flex-wrap gap-2">
296
+ {localPolicy.excludedPaths.map((path) => (
297
+ <Badge key={path} variant="secondary" className="flex items-center gap-1">
298
+ {path}
299
+ <button
300
+ onClick={() => removePath(path)}
301
+ disabled={isLoading}
302
+ className="ml-1 hover:text-destructive"
303
+ >
304
+ <X className="w-3 h-3" />
305
+ </button>
306
+ </Badge>
307
+ ))}
308
+ </div>
309
+ </div>
310
+
311
+ <div className="space-y-3">
312
+ <Label>Hash exclus (whitelist)</Label>
313
+ <div className="flex gap-2">
314
+ <Input
315
+ placeholder="SHA256 hash"
316
+ value={newHash}
317
+ onChange={(e) => setNewHash(e.target.value)}
318
+ onKeyDown={(e) => e.key === "Enter" && addHash()}
319
+ disabled={isLoading}
320
+ className="flex-1"
321
+ />
322
+ <Button onClick={addHash} disabled={isLoading} size="sm">
323
+ <Plus className="w-4 h-4" />
324
+ </Button>
325
+ </div>
326
+ <div className="flex flex-wrap gap-2">
327
+ {localPolicy.whitelistedHashes.map((hash) => (
328
+ <Badge key={hash} variant="secondary" className="flex items-center gap-1">
329
+ {hash.substring(0, 12)}...
330
+ <button
331
+ onClick={() => removeHash(hash)}
332
+ disabled={isLoading}
333
+ className="ml-1 hover:text-destructive"
334
+ >
335
+ <X className="w-3 h-3" />
336
+ </button>
337
+ </Badge>
338
+ ))}
339
+ </div>
340
+ </div>
341
+ </TabsContent>
342
+
343
+ <TabsContent value="actions" className="p-6 space-y-6">
344
+ <div className="space-y-2">
345
+ <Label>Action sur detection</Label>
346
+ <Select
347
+ value={localPolicy.detectionAction}
348
+ onValueChange={(value) => updatePolicy({ detectionAction: value as DetectionAction })}
349
+ disabled={isLoading}
350
+ >
351
+ <SelectTrigger>
352
+ <SelectValue />
353
+ </SelectTrigger>
354
+ <SelectContent>
355
+ {detectionActionOptions.map((opt) => (
356
+ <SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>
357
+ ))}
358
+ </SelectContent>
359
+ </Select>
360
+ </div>
361
+
362
+ <div className="flex items-center justify-between">
363
+ <div className="space-y-0.5">
364
+ <Label>Logger dans l'audit trail</Label>
365
+ <p className="text-sm text-muted-foreground">Enregistrer toutes les detections</p>
366
+ </div>
367
+ <Switch
368
+ checked={localPolicy.auditLog}
369
+ onCheckedChange={(checked) => updatePolicy({ auditLog: checked })}
370
+ disabled={isLoading}
371
+ />
372
+ </div>
373
+ </TabsContent>
374
+
375
+ <TabsContent value="notifications" className="p-6 space-y-6">
376
+ <div className="flex items-center justify-between">
377
+ <div className="space-y-0.5">
378
+ <Label>Notifier l'administrateur</Label>
379
+ <p className="text-sm text-muted-foreground">Alerter en cas de menace detectee</p>
380
+ </div>
381
+ <Switch
382
+ checked={localPolicy.notifyAdmin}
383
+ onCheckedChange={(checked) => updatePolicy({ notifyAdmin: checked })}
384
+ disabled={isLoading}
385
+ />
386
+ </div>
387
+
388
+ <div className="flex items-center justify-between">
389
+ <div className="space-y-0.5">
390
+ <Label>Notifier l'utilisateur</Label>
391
+ <p className="text-sm text-muted-foreground">Informer l'utilisateur de l'action prise</p>
392
+ </div>
393
+ <Switch
394
+ checked={localPolicy.notifyUser}
395
+ onCheckedChange={(checked) => updatePolicy({ notifyUser: checked })}
396
+ disabled={isLoading}
397
+ />
398
+ </div>
399
+ </TabsContent>
400
+ </Tabs>
401
+
402
+ <div className="flex items-center justify-between p-6 border-t border-border/50">
403
+ <Button variant="outline" onClick={handleResetDefaults} disabled={isLoading}>
404
+ <RotateCcw className="w-4 h-4 mr-2" />
405
+ Restaurer defauts
406
+ </Button>
407
+ <div className="flex gap-3">
408
+ <Button variant="outline" onClick={onCancel} disabled={isLoading}>
409
+ Annuler
410
+ </Button>
411
+ <Button onClick={handleSave} disabled={isLoading}>
412
+ Sauvegarder
413
+ </Button>
414
+ </div>
415
+ </div>
416
+ </Card>
417
+ )
418
+ }
@@ -0,0 +1,232 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+ import { ScanReportGenerator } from "./index"
3
+
4
+ const meta = {
5
+ title: "Blocks/Antivirus/ScanReportGenerator",
6
+ component: ScanReportGenerator,
7
+ parameters: {
8
+ layout: "padded",
9
+ },
10
+ tags: ["autodocs"],
11
+ } satisfies Meta<typeof ScanReportGenerator>
12
+
13
+ export default meta
14
+ type Story = StoryObj<typeof meta>
15
+
16
+ const defaultPreview = {
17
+ totalScans: 1247,
18
+ totalThreats: 34,
19
+ detectionRate: 98.5,
20
+ quarantinedFiles: 28,
21
+ threatsBySeverity: [
22
+ { severity: "critical", count: 3 },
23
+ { severity: "high", count: 8 },
24
+ { severity: "medium", count: 15 },
25
+ { severity: "low", count: 8 },
26
+ ],
27
+ complianceChecks: [
28
+ {
29
+ name: "Antivirus actif",
30
+ passed: true,
31
+ details: "ClamAV opérationnel avec dernière mise à jour il y a 2h",
32
+ },
33
+ {
34
+ name: "Scans automatisés",
35
+ passed: true,
36
+ details: "Planification quotidienne configurée et active",
37
+ },
38
+ {
39
+ name: "Quarantaine sécurisée",
40
+ passed: true,
41
+ details: "Isolation cryptographique AES-256-GCM activée",
42
+ },
43
+ {
44
+ name: "Audit trail",
45
+ passed: true,
46
+ details: "Logs immutables avec blockchain interne",
47
+ },
48
+ ],
49
+ weeklyTrend: [
50
+ { week: "S1", scans: 280, threats: 7 },
51
+ { week: "S2", scans: 295, threats: 9 },
52
+ { week: "S3", scans: 340, threats: 11 },
53
+ { week: "S4", scans: 332, threats: 7 },
54
+ ],
55
+ }
56
+
57
+ export const Default: Story = {
58
+ args: {
59
+ preview: defaultPreview,
60
+ onGenerate: (config: any) => {
61
+ console.log("Generating report with config:", config)
62
+ },
63
+ onSchedule: (config: any, frequency: string) => {
64
+ console.log("Scheduling report:", { config, frequency })
65
+ },
66
+ onSendEmail: (config: any, email: string) => {
67
+ console.log("Sending report to:", email)
68
+ },
69
+ },
70
+ }
71
+
72
+ export const Generating: Story = {
73
+ args: {
74
+ ...Default.args,
75
+ isGenerating: true,
76
+ generationProgress: 65,
77
+ },
78
+ }
79
+
80
+ export const ISO27001Report: Story = {
81
+ args: {
82
+ ...Default.args,
83
+ config: {
84
+ startDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
85
+ endDate: new Date(),
86
+ format: "pdf",
87
+ type: "iso27001_compliance",
88
+ includeSections: {
89
+ generalStats: true,
90
+ threatDetails: true,
91
+ scannedFiles: true,
92
+ actionsTaken: true,
93
+ recommendations: true,
94
+ },
95
+ },
96
+ },
97
+ }
98
+
99
+ export const HDSAudit: Story = {
100
+ args: {
101
+ ...Default.args,
102
+ config: {
103
+ startDate: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000),
104
+ endDate: new Date(),
105
+ format: "pdf",
106
+ type: "hds_audit",
107
+ includeSections: {
108
+ generalStats: true,
109
+ threatDetails: true,
110
+ scannedFiles: false,
111
+ actionsTaken: true,
112
+ recommendations: true,
113
+ },
114
+ },
115
+ preview: {
116
+ ...defaultPreview,
117
+ totalScans: 3842,
118
+ totalThreats: 127,
119
+ detectionRate: 99.2,
120
+ quarantinedFiles: 103,
121
+ },
122
+ },
123
+ }
124
+
125
+ export const DetailedReport: Story = {
126
+ args: {
127
+ ...Default.args,
128
+ config: {
129
+ startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
130
+ endDate: new Date(),
131
+ format: "csv",
132
+ type: "detailed",
133
+ includeSections: {
134
+ generalStats: true,
135
+ threatDetails: true,
136
+ scannedFiles: true,
137
+ actionsTaken: true,
138
+ recommendations: true,
139
+ },
140
+ },
141
+ preview: {
142
+ totalScans: 287,
143
+ totalThreats: 8,
144
+ detectionRate: 97.8,
145
+ quarantinedFiles: 6,
146
+ threatsBySeverity: [
147
+ { severity: "critical", count: 1 },
148
+ { severity: "high", count: 2 },
149
+ { severity: "medium", count: 3 },
150
+ { severity: "low", count: 2 },
151
+ ],
152
+ complianceChecks: defaultPreview.complianceChecks,
153
+ weeklyTrend: [{ week: "S1", scans: 287, threats: 8 }],
154
+ },
155
+ },
156
+ }
157
+
158
+ export const JSONExport: Story = {
159
+ args: {
160
+ ...Default.args,
161
+ config: {
162
+ startDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
163
+ endDate: new Date(),
164
+ format: "json",
165
+ type: "executive_summary",
166
+ includeSections: {
167
+ generalStats: true,
168
+ threatDetails: false,
169
+ scannedFiles: false,
170
+ actionsTaken: false,
171
+ recommendations: true,
172
+ },
173
+ },
174
+ },
175
+ }
176
+
177
+ export const NoThreats: Story = {
178
+ args: {
179
+ ...Default.args,
180
+ preview: {
181
+ totalScans: 1050,
182
+ totalThreats: 0,
183
+ detectionRate: 100,
184
+ quarantinedFiles: 0,
185
+ threatsBySeverity: [
186
+ { severity: "critical", count: 0 },
187
+ { severity: "high", count: 0 },
188
+ { severity: "medium", count: 0 },
189
+ { severity: "low", count: 0 },
190
+ ],
191
+ complianceChecks: defaultPreview.complianceChecks,
192
+ weeklyTrend: [
193
+ { week: "S1", scans: 250, threats: 0 },
194
+ { week: "S2", scans: 260, threats: 0 },
195
+ { week: "S3", scans: 270, threats: 0 },
196
+ { week: "S4", scans: 270, threats: 0 },
197
+ ],
198
+ },
199
+ },
200
+ }
201
+
202
+ export const HighThreatActivity: Story = {
203
+ args: {
204
+ ...Default.args,
205
+ preview: {
206
+ totalScans: 892,
207
+ totalThreats: 156,
208
+ detectionRate: 94.3,
209
+ quarantinedFiles: 142,
210
+ threatsBySeverity: [
211
+ { severity: "critical", count: 12 },
212
+ { severity: "high", count: 38 },
213
+ { severity: "medium", count: 67 },
214
+ { severity: "low", count: 39 },
215
+ ],
216
+ complianceChecks: [
217
+ ...defaultPreview.complianceChecks.slice(0, 3),
218
+ {
219
+ name: "Taux de détection",
220
+ passed: false,
221
+ details: "Taux inférieur au seuil requis de 95%",
222
+ },
223
+ ],
224
+ weeklyTrend: [
225
+ { week: "S1", scans: 220, threats: 28 },
226
+ { week: "S2", scans: 218, threats: 42 },
227
+ { week: "S3", scans: 225, threats: 51 },
228
+ { week: "S4", scans: 229, threats: 35 },
229
+ ],
230
+ },
231
+ },
232
+ }