@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,195 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+ import { ClamAVServiceStatus } from "./index"
3
+
4
+ const meta = {
5
+ title: "Blocks/Antivirus/ClamAVServiceStatus",
6
+ component: ClamAVServiceStatus,
7
+ parameters: {
8
+ layout: "padded"
9
+ },
10
+ tags: ["autodocs"]
11
+ } satisfies Meta<typeof ClamAVServiceStatus>
12
+
13
+ export default meta
14
+ type Story = StoryObj<typeof meta>
15
+
16
+ const generateUptimeHistory = (pattern: "healthy" | "degraded") => {
17
+ const hours = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
18
+
19
+ if (pattern === "healthy") {
20
+ return hours.map(hour => ({
21
+ hour,
22
+ status: Math.random() > 0.95 ? ("degraded" as const) : ("up" as const)
23
+ }))
24
+ }
25
+
26
+ return hours.map(hour => ({
27
+ hour,
28
+ status: Math.random() > 0.7 ? ("up" as const) : Math.random() > 0.5 ? ("degraded" as const) : ("down" as const)
29
+ }))
30
+ }
31
+
32
+ export const Healthy: Story = {
33
+ args: {
34
+ status: "running",
35
+ version: "1.0.3",
36
+ uptime: 259200, // 3 days
37
+ metrics: {
38
+ cpuUsage: 2.5,
39
+ memoryUsage: 512,
40
+ threads: 4,
41
+ scanQueue: 0,
42
+ activeConnections: 3
43
+ },
44
+ signatures: {
45
+ version: "27456",
46
+ count: 8764532,
47
+ lastUpdate: new Date(Date.now() - 3600000), // 1h ago
48
+ freshclamStatus: "running",
49
+ nextScheduledUpdate: new Date(Date.now() + 3600000) // in 1h
50
+ },
51
+ socketType: "tcp",
52
+ socketPath: "3310",
53
+ uptimeHistory: generateUptimeHistory("healthy"),
54
+ onRestart: () => console.log("Restart clicked"),
55
+ onUpdateSignatures: () => console.log("Update signatures clicked"),
56
+ onViewLogs: () => console.log("View logs clicked")
57
+ }
58
+ }
59
+
60
+ export const Degraded: Story = {
61
+ args: {
62
+ status: "running",
63
+ version: "1.0.3",
64
+ uptime: 86400, // 1 day
65
+ metrics: {
66
+ cpuUsage: 78.3,
67
+ memoryUsage: 892,
68
+ threads: 8,
69
+ scanQueue: 12,
70
+ activeConnections: 15
71
+ },
72
+ signatures: {
73
+ version: "27450",
74
+ count: 8762104,
75
+ lastUpdate: new Date(Date.now() - 172800000), // 2 days ago
76
+ freshclamStatus: "error",
77
+ nextScheduledUpdate: new Date(Date.now() + 3600000)
78
+ },
79
+ socketType: "unix",
80
+ socketPath: "/var/run/clamav/clamd.sock",
81
+ uptimeHistory: generateUptimeHistory("degraded"),
82
+ onRestart: () => console.log("Restart clicked"),
83
+ onUpdateSignatures: () => console.log("Update signatures clicked"),
84
+ onViewLogs: () => console.log("View logs clicked")
85
+ }
86
+ }
87
+
88
+ export const Stopped: Story = {
89
+ args: {
90
+ status: "stopped",
91
+ version: "1.0.3",
92
+ uptime: 0,
93
+ metrics: {
94
+ cpuUsage: 0,
95
+ memoryUsage: 0,
96
+ threads: 0,
97
+ scanQueue: 0,
98
+ activeConnections: 0
99
+ },
100
+ signatures: {
101
+ version: "27456",
102
+ count: 8764532,
103
+ lastUpdate: new Date(Date.now() - 7200000), // 2h ago
104
+ freshclamStatus: "stopped"
105
+ },
106
+ socketType: "tcp",
107
+ socketPath: "3310",
108
+ uptimeHistory: [],
109
+ onRestart: () => console.log("Restart clicked"),
110
+ onUpdateSignatures: () => console.log("Update signatures clicked"),
111
+ onViewLogs: () => console.log("View logs clicked")
112
+ }
113
+ }
114
+
115
+ export const Error: Story = {
116
+ args: {
117
+ status: "error",
118
+ version: "1.0.3",
119
+ uptime: 0,
120
+ metrics: {
121
+ cpuUsage: 0,
122
+ memoryUsage: 128,
123
+ threads: 1,
124
+ scanQueue: 0,
125
+ activeConnections: 0
126
+ },
127
+ signatures: {
128
+ version: "27456",
129
+ count: 8764532,
130
+ lastUpdate: new Date(Date.now() - 86400000), // 1 day ago
131
+ freshclamStatus: "error"
132
+ },
133
+ socketType: "tcp",
134
+ socketPath: "3310",
135
+ uptimeHistory: generateUptimeHistory("degraded"),
136
+ onRestart: () => console.log("Restart clicked"),
137
+ onUpdateSignatures: () => console.log("Update signatures clicked"),
138
+ onViewLogs: () => console.log("View logs clicked")
139
+ }
140
+ }
141
+
142
+ export const Restarting: Story = {
143
+ args: {
144
+ status: "restarting",
145
+ version: "1.0.3",
146
+ uptime: 0,
147
+ metrics: {
148
+ cpuUsage: 15.2,
149
+ memoryUsage: 256,
150
+ threads: 2,
151
+ scanQueue: 0,
152
+ activeConnections: 0
153
+ },
154
+ signatures: {
155
+ version: "27456",
156
+ count: 8764532,
157
+ lastUpdate: new Date(Date.now() - 3600000),
158
+ freshclamStatus: "stopped"
159
+ },
160
+ socketType: "tcp",
161
+ socketPath: "3310",
162
+ uptimeHistory: generateUptimeHistory("healthy"),
163
+ onRestart: () => console.log("Restart clicked"),
164
+ onUpdateSignatures: () => console.log("Update signatures clicked"),
165
+ onViewLogs: () => console.log("View logs clicked")
166
+ }
167
+ }
168
+
169
+ export const UpdatingSignatures: Story = {
170
+ args: {
171
+ status: "running",
172
+ version: "1.0.3",
173
+ uptime: 172800, // 2 days
174
+ metrics: {
175
+ cpuUsage: 45.8,
176
+ memoryUsage: 678,
177
+ threads: 6,
178
+ scanQueue: 3,
179
+ activeConnections: 5
180
+ },
181
+ signatures: {
182
+ version: "27456",
183
+ count: 8764532,
184
+ lastUpdate: new Date(Date.now() - 1800000), // 30min ago
185
+ freshclamStatus: "updating",
186
+ nextScheduledUpdate: new Date(Date.now() + 1800000)
187
+ },
188
+ socketType: "tcp",
189
+ socketPath: "3310",
190
+ uptimeHistory: generateUptimeHistory("healthy"),
191
+ onRestart: () => console.log("Restart clicked"),
192
+ onUpdateSignatures: () => console.log("Update signatures clicked"),
193
+ onViewLogs: () => console.log("View logs clicked")
194
+ }
195
+ }
@@ -0,0 +1,370 @@
1
+ "use client"
2
+
3
+ import { Activity, AlertTriangle, CheckCircle, Clock, Database, HardDrive, Loader2, RefreshCw, Terminal, Wifi } from "lucide-react"
4
+ import { Badge } from "../../components/badge"
5
+ import { Button } from "../../components/button"
6
+ import { Card } from "../../components/card"
7
+ import { Progress } from "../../components/progress"
8
+ import { cn } from "../../utils/cn"
9
+
10
+ export type DaemonStatus = "running" | "stopped" | "error" | "restarting"
11
+ export type FreshclamStatus = "running" | "stopped" | "error" | "updating"
12
+
13
+ export interface ClamAVMetrics {
14
+ cpuUsage: number
15
+ memoryUsage: number
16
+ threads: number
17
+ scanQueue: number
18
+ activeConnections: number
19
+ }
20
+
21
+ export interface SignatureInfo {
22
+ version: string
23
+ count: number
24
+ lastUpdate: Date
25
+ freshclamStatus: FreshclamStatus
26
+ nextScheduledUpdate?: Date
27
+ }
28
+
29
+ export interface UptimeEntry {
30
+ hour: string
31
+ status: "up" | "degraded" | "down"
32
+ }
33
+
34
+ export interface ClamAVServiceStatusProps {
35
+ status?: DaemonStatus
36
+ version?: string
37
+ uptime?: number // seconds
38
+ metrics?: ClamAVMetrics
39
+ signatures?: SignatureInfo
40
+ socketType?: "tcp" | "unix"
41
+ socketPath?: string
42
+ uptimeHistory?: UptimeEntry[]
43
+ onRestart?: () => void
44
+ onUpdateSignatures?: () => void
45
+ onViewLogs?: () => void
46
+ className?: string
47
+ }
48
+
49
+ const formatUptime = (seconds: number): string => {
50
+ const days = Math.floor(seconds / 86400)
51
+ const hours = Math.floor((seconds % 86400) / 3600)
52
+ const minutes = Math.floor((seconds % 3600) / 60)
53
+
54
+ if (days > 0) return `${days}d ${hours}h`
55
+ if (hours > 0) return `${hours}h ${minutes}m`
56
+ return `${minutes}m`
57
+ }
58
+
59
+ const formatDate = (date: Date): string => {
60
+ const now = new Date()
61
+ const diff = now.getTime() - date.getTime()
62
+ const minutes = Math.floor(diff / 60000)
63
+ const hours = Math.floor(diff / 3600000)
64
+ const days = Math.floor(diff / 86400000)
65
+
66
+ if (minutes < 60) return `${minutes}m ago`
67
+ if (hours < 24) return `${hours}h ago`
68
+ return `${days}d ago`
69
+ }
70
+
71
+ const statusConfig = {
72
+ running: {
73
+ color: "text-green-500",
74
+ bgColor: "bg-green-500/10",
75
+ borderColor: "border-green-500/20",
76
+ icon: CheckCircle,
77
+ label: "Running"
78
+ },
79
+ stopped: {
80
+ color: "text-gray-500",
81
+ bgColor: "bg-gray-500/10",
82
+ borderColor: "border-gray-500/20",
83
+ icon: Activity,
84
+ label: "Stopped"
85
+ },
86
+ error: {
87
+ color: "text-red-500",
88
+ bgColor: "bg-red-500/10",
89
+ borderColor: "border-red-500/20",
90
+ icon: AlertTriangle,
91
+ label: "Error"
92
+ },
93
+ restarting: {
94
+ color: "text-yellow-500",
95
+ bgColor: "bg-yellow-500/10",
96
+ borderColor: "border-yellow-500/20",
97
+ icon: Loader2,
98
+ label: "Restarting"
99
+ }
100
+ }
101
+
102
+ const freshclamConfig = {
103
+ running: { color: "text-green-500", label: "Running" },
104
+ stopped: { color: "text-gray-500", label: "Stopped" },
105
+ error: { color: "text-red-500", label: "Error" },
106
+ updating: { color: "text-blue-500", label: "Updating" }
107
+ }
108
+
109
+ const uptimeStatusConfig = {
110
+ up: "bg-green-500",
111
+ degraded: "bg-yellow-500",
112
+ down: "bg-red-500"
113
+ }
114
+
115
+ export const ClamAVServiceStatus = ({
116
+ status = "running",
117
+ version = "1.0.3",
118
+ uptime = 259200, // 3 days
119
+ metrics = {
120
+ cpuUsage: 2.5,
121
+ memoryUsage: 512,
122
+ threads: 4,
123
+ scanQueue: 0,
124
+ activeConnections: 3
125
+ },
126
+ signatures = {
127
+ version: "27456",
128
+ count: 8764532,
129
+ lastUpdate: new Date(Date.now() - 3600000), // 1h ago
130
+ freshclamStatus: "running",
131
+ nextScheduledUpdate: new Date(Date.now() + 3600000) // in 1h
132
+ },
133
+ socketType = "tcp",
134
+ socketPath = "3310",
135
+ uptimeHistory = [],
136
+ onRestart,
137
+ onUpdateSignatures,
138
+ onViewLogs,
139
+ className
140
+ }: ClamAVServiceStatusProps) => {
141
+ const config = statusConfig[status]
142
+ const StatusIcon = config.icon
143
+
144
+ return (
145
+ <div className={cn("space-y-4", className)}>
146
+ {/* Main Status Card */}
147
+ <Card className={cn("p-6 border-2", config.borderColor, config.bgColor)}>
148
+ <div className="flex items-start justify-between gap-4">
149
+ <div className="flex items-start gap-4">
150
+ <div className={cn("p-3 rounded-xl", config.bgColor)}>
151
+ <StatusIcon className={cn("w-8 h-8", config.color, status === "restarting" && "animate-spin")} />
152
+ </div>
153
+ <div className="space-y-1">
154
+ <div className="flex items-center gap-2">
155
+ <h3 className="text-2xl font-semibold">ClamAV Daemon</h3>
156
+ <Badge variant={status === "error" ? "destructive" : "secondary"} className={status === "running" ? "bg-green-500/10 text-green-600 border-green-500/20" : ""}>
157
+ {config.label}
158
+ </Badge>
159
+ </div>
160
+ <div className="flex items-center gap-4 text-sm text-muted-foreground">
161
+ <span>Version {version}</span>
162
+ <span className="flex items-center gap-1">
163
+ <Clock className="w-3.5 h-3.5" />
164
+ Uptime: {formatUptime(uptime)}
165
+ </span>
166
+ </div>
167
+ </div>
168
+ </div>
169
+
170
+ <div className="flex items-center gap-2">
171
+ <Button variant="outline" size="sm" onClick={onViewLogs}>
172
+ <Terminal className="w-4 h-4 mr-2" />
173
+ Logs
174
+ </Button>
175
+ <Button variant="outline" size="sm" onClick={onRestart} disabled={status === "restarting"}>
176
+ <RefreshCw className={cn("w-4 h-4 mr-2", status === "restarting" && "animate-spin")} />
177
+ Restart
178
+ </Button>
179
+ </div>
180
+ </div>
181
+ </Card>
182
+
183
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
184
+ {/* System Metrics */}
185
+ <Card className="p-6">
186
+ <div className="flex items-center gap-2 mb-4">
187
+ <Activity className="w-5 h-5 text-primary" />
188
+ <h4 className="font-semibold">System Metrics</h4>
189
+ </div>
190
+
191
+ <div className="space-y-4">
192
+ <div>
193
+ <div className="flex justify-between text-sm mb-1.5">
194
+ <span className="text-muted-foreground">CPU Usage</span>
195
+ <span className="font-medium">{metrics.cpuUsage.toFixed(1)}%</span>
196
+ </div>
197
+ <Progress
198
+ value={metrics.cpuUsage}
199
+ className={cn(
200
+ "h-2",
201
+ metrics.cpuUsage > 80 && "bg-red-500/20",
202
+ metrics.cpuUsage > 50 && metrics.cpuUsage <= 80 && "bg-yellow-500/20"
203
+ )}
204
+ />
205
+ </div>
206
+
207
+ <div>
208
+ <div className="flex justify-between text-sm mb-1.5">
209
+ <span className="text-muted-foreground">Memory Usage</span>
210
+ <span className="font-medium">{metrics.memoryUsage} MB</span>
211
+ </div>
212
+ <Progress
213
+ value={(metrics.memoryUsage / 1024) * 100}
214
+ className={cn(
215
+ "h-2",
216
+ metrics.memoryUsage > 800 && "bg-red-500/20",
217
+ metrics.memoryUsage > 600 && metrics.memoryUsage <= 800 && "bg-yellow-500/20"
218
+ )}
219
+ />
220
+ </div>
221
+
222
+ <div className="grid grid-cols-3 gap-4 pt-2">
223
+ <div className="space-y-1">
224
+ <div className="text-xs text-muted-foreground">Threads</div>
225
+ <div className="text-2xl font-semibold">{metrics.threads}</div>
226
+ </div>
227
+ <div className="space-y-1">
228
+ <div className="text-xs text-muted-foreground">Scan Queue</div>
229
+ <div className="text-2xl font-semibold">{metrics.scanQueue}</div>
230
+ </div>
231
+ <div className="space-y-1">
232
+ <div className="text-xs text-muted-foreground">Connections</div>
233
+ <div className="text-2xl font-semibold flex items-center gap-1">
234
+ {metrics.activeConnections}
235
+ <Wifi className="w-4 h-4 text-muted-foreground" />
236
+ </div>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </Card>
241
+
242
+ {/* Signature Database */}
243
+ <Card className="p-6">
244
+ <div className="flex items-center justify-between mb-4">
245
+ <div className="flex items-center gap-2">
246
+ <Database className="w-5 h-5 text-primary" />
247
+ <h4 className="font-semibold">Signature Database</h4>
248
+ </div>
249
+ <Button variant="outline" size="sm" onClick={onUpdateSignatures}>
250
+ <RefreshCw className="w-4 h-4 mr-2" />
251
+ Update
252
+ </Button>
253
+ </div>
254
+
255
+ <div className="space-y-4">
256
+ <div className="grid grid-cols-2 gap-4">
257
+ <div className="space-y-1">
258
+ <div className="text-xs text-muted-foreground">Version</div>
259
+ <div className="text-lg font-semibold">{signatures.version}</div>
260
+ </div>
261
+ <div className="space-y-1">
262
+ <div className="text-xs text-muted-foreground">Signatures</div>
263
+ <div className="text-lg font-semibold">{signatures.count.toLocaleString()}</div>
264
+ </div>
265
+ </div>
266
+
267
+ <div className="space-y-2 pt-2 border-t">
268
+ <div className="flex justify-between text-sm">
269
+ <span className="text-muted-foreground">Last Updated</span>
270
+ <span className="font-medium">{formatDate(signatures.lastUpdate)}</span>
271
+ </div>
272
+ {signatures.nextScheduledUpdate && (
273
+ <div className="flex justify-between text-sm">
274
+ <span className="text-muted-foreground">Next Update</span>
275
+ <span className="font-medium">in {formatDate(new Date(Date.now() - (Date.now() - signatures.nextScheduledUpdate.getTime())))}</span>
276
+ </div>
277
+ )}
278
+ </div>
279
+
280
+ <div className="flex items-center justify-between pt-2 border-t">
281
+ <span className="text-sm text-muted-foreground">Freshclam Status</span>
282
+ <Badge
283
+ variant={
284
+ signatures.freshclamStatus === "error" ? "destructive" :
285
+ signatures.freshclamStatus === "updating" ? "default" :
286
+ "secondary"
287
+ }
288
+ className={signatures.freshclamStatus === "running" ? "bg-green-500/10 text-green-600 border-green-500/20" : ""}
289
+ >
290
+ <span className={cn(freshclamConfig[signatures.freshclamStatus].color)}>
291
+ {freshclamConfig[signatures.freshclamStatus].label}
292
+ </span>
293
+ </Badge>
294
+ </div>
295
+ </div>
296
+ </Card>
297
+ </div>
298
+
299
+ {/* Connection Info & Uptime History */}
300
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
301
+ <Card className="p-6">
302
+ <div className="flex items-center gap-2 mb-4">
303
+ <Wifi className="w-5 h-5 text-primary" />
304
+ <h4 className="font-semibold">Connection</h4>
305
+ </div>
306
+
307
+ <div className="space-y-3">
308
+ <div className="flex justify-between">
309
+ <span className="text-sm text-muted-foreground">Socket Type</span>
310
+ <Badge variant="outline">{socketType.toUpperCase()}</Badge>
311
+ </div>
312
+ <div className="flex justify-between">
313
+ <span className="text-sm text-muted-foreground">{socketType === "tcp" ? "Port" : "Path"}</span>
314
+ <span className="text-sm font-mono font-medium">{socketPath}</span>
315
+ </div>
316
+ <div className="flex justify-between pt-2 border-t">
317
+ <span className="text-sm text-muted-foreground">Active Connections</span>
318
+ <span className="text-lg font-semibold">{metrics.activeConnections}</span>
319
+ </div>
320
+ </div>
321
+ </Card>
322
+
323
+ <Card className="p-6 lg:col-span-2">
324
+ <div className="flex items-center gap-2 mb-4">
325
+ <HardDrive className="w-5 h-5 text-primary" />
326
+ <h4 className="font-semibold">Uptime History (Last 7 Days)</h4>
327
+ </div>
328
+
329
+ {uptimeHistory.length > 0 ? (
330
+ <div className="flex items-end justify-between gap-1 h-20">
331
+ {uptimeHistory.map((entry, index) => (
332
+ <div key={index} className="flex-1 flex flex-col items-center gap-1">
333
+ <div
334
+ className={cn(
335
+ "w-full rounded-t transition-all",
336
+ uptimeStatusConfig[entry.status],
337
+ entry.status === "up" && "h-full",
338
+ entry.status === "degraded" && "h-3/4",
339
+ entry.status === "down" && "h-1/4"
340
+ )}
341
+ />
342
+ <span className="text-[10px] text-muted-foreground">{entry.hour}</span>
343
+ </div>
344
+ ))}
345
+ </div>
346
+ ) : (
347
+ <div className="h-20 flex items-center justify-center text-sm text-muted-foreground">
348
+ No uptime history available
349
+ </div>
350
+ )}
351
+
352
+ <div className="flex items-center justify-center gap-4 mt-4 pt-4 border-t">
353
+ <div className="flex items-center gap-1.5">
354
+ <div className="w-3 h-3 rounded bg-green-500" />
355
+ <span className="text-xs text-muted-foreground">Up</span>
356
+ </div>
357
+ <div className="flex items-center gap-1.5">
358
+ <div className="w-3 h-3 rounded bg-yellow-500" />
359
+ <span className="text-xs text-muted-foreground">Degraded</span>
360
+ </div>
361
+ <div className="flex items-center gap-1.5">
362
+ <div className="w-3 h-3 rounded bg-red-500" />
363
+ <span className="text-xs text-muted-foreground">Down</span>
364
+ </div>
365
+ </div>
366
+ </Card>
367
+ </div>
368
+ </div>
369
+ )
370
+ }