@wakastellar/ui 2.3.4 → 2.4.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.
- package/dist/blocks/dashboard/index.d.ts +4 -1
- package/dist/blocks/empty-states/index.d.ts +4 -1
- package/dist/blocks/error-pages/index.d.ts +4 -1
- package/dist/blocks/index.d.ts +1 -1
- package/dist/blocks/landing/index.d.ts +4 -1
- package/dist/blocks/pricing/index.d.ts +5 -1
- package/dist/blocks/sidebar/index.d.ts +5 -1
- package/dist/index.cjs.js +130 -130
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +7905 -7856
- package/dist/stories/Button.d.ts +14 -0
- package/dist/stories/Button.stories.d.ts +8 -0
- package/dist/stories/Header.d.ts +11 -0
- package/dist/stories/Header.stories.d.ts +6 -0
- package/dist/stories/Page.d.ts +2 -0
- package/dist/stories/Page.stories.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/link.d.ts +23 -0
- package/package.json +11 -3
- package/src/blocks/activity-timeline/ActivityTimeline.stories.tsx +460 -0
- package/src/blocks/apm-overview/APMOverview.stories.tsx +435 -0
- package/src/blocks/auth-2fa/Auth2FA.stories.tsx +308 -0
- package/src/blocks/calendar-view/CalendarView.stories.tsx +398 -0
- package/src/blocks/chat/Chat.stories.tsx +466 -0
- package/src/blocks/chat-interface/ChatInterface.stories.tsx +412 -0
- package/src/blocks/checkout-flow/CheckoutFlow.stories.tsx +408 -0
- package/src/blocks/cicd-builder/CICDBuilder.stories.tsx +499 -0
- package/src/blocks/cloud-cost-dashboard/CloudCostDashboard.stories.tsx +356 -0
- package/src/blocks/container-orchestrator/ContainerOrchestrator.stories.tsx +461 -0
- package/src/blocks/dashboard/Dashboard.stories.tsx +559 -0
- package/src/blocks/dashboard/index.tsx +68 -27
- package/src/blocks/dashboard-kpi/DashboardKPI.stories.tsx +422 -0
- package/src/blocks/database-admin/DatabaseAdmin.stories.tsx +393 -0
- package/src/blocks/deployment-dashboard/DeploymentDashboard.stories.tsx +457 -0
- package/src/blocks/empty-states/EmptyStates.stories.tsx +467 -0
- package/src/blocks/empty-states/index.tsx +26 -8
- package/src/blocks/error-pages/ErrorPages.stories.tsx +401 -0
- package/src/blocks/error-pages/index.tsx +26 -8
- package/src/blocks/faq/FAQ.stories.tsx +416 -0
- package/src/blocks/file-manager/FileManager.stories.tsx +413 -0
- package/src/blocks/footer/Footer.stories.tsx +328 -0
- package/src/blocks/gitops-sync-status/GitOpsSyncStatus.stories.tsx +529 -0
- package/src/blocks/header/WakaHeader.stories.tsx +455 -0
- package/src/blocks/headtab/Headtab.stories.tsx +369 -0
- package/src/blocks/i18n-editor/I18nEditor.stories.tsx +451 -0
- package/src/blocks/incident-manager/IncidentManager.stories.tsx +464 -0
- package/src/blocks/index.ts +1 -1
- package/src/blocks/infrastructure-map/InfrastructureMap.stories.tsx +533 -0
- package/src/blocks/kanban-board/KanbanBoard.stories.tsx +494 -0
- package/src/blocks/landing/WakaLanding.stories.tsx +449 -0
- package/src/blocks/landing/index.tsx +125 -66
- package/src/blocks/language-selector/LanguageSelector.stories.tsx +345 -0
- package/src/blocks/layout/Layout.stories.tsx +373 -0
- package/src/blocks/login/Login.stories.tsx +443 -0
- package/src/blocks/on-call-schedule/OnCallSchedule.stories.tsx +381 -0
- package/src/blocks/player-profile/PlayerProfile.stories.tsx +316 -0
- package/src/blocks/pricing/WakaPricing.stories.tsx +530 -0
- package/src/blocks/pricing/index.tsx +38 -4
- package/src/blocks/profile/Profile.stories.tsx +371 -0
- package/src/blocks/release-notes/ReleaseNotes.stories.tsx +431 -0
- package/src/blocks/settings/Settings.stories.tsx +520 -0
- package/src/blocks/sidebar/WakaSidebar.stories.tsx +513 -0
- package/src/blocks/sidebar/index.tsx +49 -20
- package/src/blocks/theme-creator-block/ThemeCreatorBlock.stories.tsx +370 -0
- package/src/blocks/user-management/UserManagement.stories.tsx +411 -0
- package/src/blocks/wizard/Wizard.stories.tsx +683 -0
- package/src/components/accordion/Accordion.stories.tsx +93 -0
- package/src/components/alert/Alert.stories.tsx +95 -0
- package/src/components/alert-dialog/AlertDialog.stories.tsx +126 -0
- package/src/components/aspect-ratio/AspectRatio.stories.tsx +118 -0
- package/src/components/avatar/Avatar.stories.tsx +104 -0
- package/src/components/button/Button.stories.tsx +12 -1
- package/src/components/calendar/Calendar.stories.tsx +102 -0
- package/src/components/card/Card.stories.tsx +125 -0
- package/src/components/checkbox/Checkbox.stories.tsx +100 -0
- package/src/components/code/Code.stories.tsx +402 -0
- package/src/components/collapsible/Collapsible.stories.tsx +123 -0
- package/src/components/command/Command.stories.tsx +207 -0
- package/src/components/context-menu/ContextMenu.stories.tsx +220 -0
- package/src/components/dialog/Dialog.stories.tsx +157 -0
- package/src/components/dropdown-menu/DropdownMenu.stories.tsx +225 -0
- package/src/components/form/Form.stories.tsx +413 -0
- package/src/components/hover-card/HoverCard.stories.tsx +148 -0
- package/src/components/input-otp/InputOTP.stories.tsx +255 -0
- package/src/components/label/Label.stories.tsx +68 -0
- package/src/components/menubar/Menubar.stories.tsx +278 -0
- package/src/components/navigation-menu/NavigationMenu.stories.tsx +202 -0
- package/src/components/popover/Popover.stories.tsx +199 -0
- package/src/components/progress/Progress.stories.tsx +104 -0
- package/src/components/radio-group/RadioGroup.stories.tsx +138 -0
- package/src/components/scroll-area/ScrollArea.stories.tsx +153 -0
- package/src/components/select/Select.stories.tsx +146 -0
- package/src/components/separator/Separator.stories.tsx +117 -0
- package/src/components/sheet/Sheet.stories.tsx +195 -0
- package/src/components/skeleton/Skeleton.stories.tsx +114 -0
- package/src/components/slider/Slider.stories.tsx +157 -0
- package/src/components/switch/Switch.stories.tsx +114 -0
- package/src/components/table/Table.stories.tsx +153 -0
- package/src/components/tabs/Tabs.stories.tsx +155 -0
- package/src/components/textarea/Textarea.stories.tsx +116 -0
- package/src/components/toast/Toast.stories.tsx +160 -0
- package/src/components/toggle/Toggle.stories.tsx +125 -0
- package/src/components/tooltip/Tooltip.stories.tsx +133 -0
- package/src/components/typography/Typography.stories.tsx +207 -0
- package/src/components/waka-3d-pie-chart/Waka3DPieChart.stories.tsx +308 -0
- package/src/components/waka-achievement-unlock/WakaAchievementUnlock.stories.tsx +353 -0
- package/src/components/waka-artifact-list/WakaArtifactList.stories.tsx +258 -0
- package/src/components/waka-autocomplete/WakaAutocomplete.stories.tsx +224 -0
- package/src/components/waka-badge-showcase/WakaBadgeShowcase.stories.tsx +269 -0
- package/src/components/waka-barcode/WakaBarcode.stories.tsx +227 -0
- package/src/components/waka-bottom-sheet/WakaBottomSheet.stories.tsx +408 -0
- package/src/components/waka-breadcrumb/WakaBreadcrumb.stories.tsx +237 -0
- package/src/components/waka-build-matrix/WakaBuildMatrix.stories.tsx +328 -0
- package/src/components/waka-carousel/WakaCarousel.stories.tsx +296 -0
- package/src/components/waka-charts/WakaCharts.stories.tsx +519 -0
- package/src/components/waka-color-picker/WakaColorPicker.stories.tsx +200 -0
- package/src/components/waka-combobox/WakaCombobox.stories.tsx +250 -0
- package/src/components/waka-container-list/WakaContainerList.stories.tsx +315 -0
- package/src/components/waka-contribution-graph/WakaContributionGraph.stories.tsx +354 -0
- package/src/components/waka-cost-breakdown/WakaCostBreakdown.stories.tsx +348 -0
- package/src/components/waka-daily-reward/WakaDailyReward.stories.tsx +365 -0
- package/src/components/waka-database-card/WakaDatabaseCard.stories.tsx +306 -0
- package/src/components/waka-date-range-picker/WakaDateRangePicker.stories.tsx +339 -0
- package/src/components/waka-datetime-picker/WakaDateTimePicker.stories.tsx +317 -0
- package/src/components/waka-deployment-lane/WakaDeploymentLane.stories.tsx +292 -0
- package/src/components/waka-dock/WakaDock.stories.tsx +332 -0
- package/src/components/waka-drawer/WakaDrawer.stories.tsx +437 -0
- package/src/components/waka-env-var-editor/WakaEnvVarEditor.stories.tsx +263 -0
- package/src/components/waka-error-shake/WakaErrorShake.stories.tsx +410 -0
- package/src/components/waka-file-upload/WakaFileUpload.stories.tsx +239 -0
- package/src/components/waka-flow-diagram/WakaFlowDiagram.stories.tsx +365 -0
- package/src/components/waka-funnel-chart/WakaFunnelChart.stories.tsx +281 -0
- package/src/components/waka-glow-card/WakaGlowCard.stories.tsx +274 -0
- package/src/components/waka-haptic-button/WakaHapticButton.stories.tsx +349 -0
- package/src/components/waka-health-pulse/WakaHealthPulse.stories.tsx +293 -0
- package/src/components/waka-heatmap/WakaHeatmap.stories.tsx +376 -0
- package/src/components/waka-image/WakaImage.stories.tsx +255 -0
- package/src/components/waka-incident-timeline/WakaIncidentTimeline.stories.tsx +300 -0
- package/src/components/waka-kanban/WakaKanban.stories.tsx +399 -0
- package/src/components/waka-kubernetes-overview/WakaKubernetesOverview.stories.tsx +281 -0
- package/src/components/waka-leaderboard/WakaLeaderboard.stories.tsx +300 -0
- package/src/components/waka-level-progress/WakaLevelProgress.stories.tsx +313 -0
- package/src/components/waka-loading-orbit/WakaLoadingOrbit.stories.tsx +413 -0
- package/src/components/waka-log-viewer/WakaLogViewer.stories.tsx +312 -0
- package/src/components/waka-loot-box/WakaLootBox.stories.tsx +374 -0
- package/src/components/waka-metric-sparkline/WakaMetricSparkline.stories.tsx +312 -0
- package/src/components/waka-migration-list/WakaMigrationList.stories.tsx +289 -0
- package/src/components/waka-modal/WakaModal.stories.tsx +434 -0
- package/src/components/waka-morph-button/WakaMorphButton.stories.tsx +405 -0
- package/src/components/waka-network-topology/WakaNetworkTopology.stories.tsx +364 -0
- package/src/components/waka-notifications/WakaNotifications.stories.tsx +290 -0
- package/src/components/waka-number-input/WakaNumberInput.stories.tsx +282 -0
- package/src/components/waka-pagination/WakaPagination.stories.tsx +328 -0
- package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +318 -0
- package/src/components/waka-pipeline-view/WakaPipelineView.stories.tsx +386 -0
- package/src/components/waka-player-card/WakaPlayerCard.stories.tsx +333 -0
- package/src/components/waka-pod-card/WakaPodCard.stories.tsx +435 -0
- package/src/components/waka-qrcode/WakaQRCode.stories.tsx +232 -0
- package/src/components/waka-query-explain/WakaQueryExplain.stories.tsx +407 -0
- package/src/components/waka-quest-card/WakaQuestCard.stories.tsx +394 -0
- package/src/components/waka-quota-bar/WakaQuotaBar.stories.tsx +435 -0
- package/src/components/waka-radar-score/WakaRadarScore.stories.tsx +372 -0
- package/src/components/waka-resource-gauge/WakaResourceGauge.stories.tsx +366 -0
- package/src/components/waka-rich-text-editor/WakaRichTextEditor.stories.tsx +238 -0
- package/src/components/waka-sankey-diagram/WakaSankeyDiagram.stories.tsx +389 -0
- package/src/components/waka-scratch-card/WakaScratchCard.stories.tsx +388 -0
- package/src/components/waka-secret-card/WakaSecretCard.stories.tsx +314 -0
- package/src/components/waka-segmented-control/WakaSegmentedControl.stories.tsx +309 -0
- package/src/components/waka-server-rack/WakaServerRack.stories.tsx +382 -0
- package/src/components/waka-service-graph/WakaServiceGraph.stories.tsx +262 -0
- package/src/components/waka-skeleton-wave/WakaSkeletonWave.stories.tsx +321 -0
- package/src/components/waka-skill-tree/WakaSkillTree.stories.tsx +308 -0
- package/src/components/waka-spin-wheel/WakaSpinWheel.stories.tsx +368 -0
- package/src/components/waka-spinner/WakaSpinner.stories.tsx +156 -0
- package/src/components/waka-stat/WakaStat.stories.tsx +334 -0
- package/src/components/waka-status-matrix/WakaStatusMatrix.stories.tsx +331 -0
- package/src/components/waka-stepper/WakaStepper.stories.tsx +468 -0
- package/src/components/waka-streak-counter/WakaStreakCounter.stories.tsx +235 -0
- package/src/components/waka-success-explosion/WakaSuccessExplosion.stories.tsx +389 -0
- package/src/components/waka-tabs-morph/WakaTabsMorph.stories.tsx +471 -0
- package/src/components/waka-terminal-output/WakaTerminalOutput.stories.tsx +351 -0
- package/src/components/waka-test-report/WakaTestReport.stories.tsx +322 -0
- package/src/components/waka-tilt-card/WakaTiltCard.stories.tsx +300 -0
- package/src/components/waka-time-picker/WakaTimePicker.stories.tsx +227 -0
- package/src/components/waka-timeline/WakaTimeline.stories.tsx +383 -0
- package/src/components/waka-tournament-bracket/WakaTournamentBracket.stories.tsx +375 -0
- package/src/components/waka-trace-viewer/WakaTraceViewer.stories.tsx +445 -0
- package/src/components/waka-tree/WakaTree.stories.tsx +359 -0
- package/src/components/waka-treemap-chart/WakaTreemapChart.stories.tsx +378 -0
- package/src/components/waka-typewriter/WakaTypewriter.stories.tsx +366 -0
- package/src/components/waka-versus-card/WakaVersusCard.stories.tsx +530 -0
- package/src/components/waka-video/WakaVideo.stories.tsx +203 -0
- package/src/components/waka-virtual-list/WakaVirtualList.stories.tsx +273 -0
- package/src/components/waka-xp-bar/WakaXPBar.stories.tsx +305 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { WakaNetworkTopology } from './index'
|
|
3
|
+
import type { NetworkNode, NetworkEdge } from './index'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
const simpleTopologyNodes: NetworkNode[] = [
|
|
7
|
+
{ id: 'lb', label: 'Load Balancer', type: 'loadbalancer', status: 'healthy', x: 400, y: 100 },
|
|
8
|
+
{ id: 'web1', label: 'Web Server 1', type: 'server', status: 'healthy', x: 200, y: 250, metrics: { cpu: 45, memory: 62, connections: 1240 } },
|
|
9
|
+
{ id: 'web2', label: 'Web Server 2', type: 'server', status: 'healthy', x: 400, y: 250, metrics: { cpu: 52, memory: 58, connections: 1180 } },
|
|
10
|
+
{ id: 'web3', label: 'Web Server 3', type: 'server', status: 'warning', x: 600, y: 250, metrics: { cpu: 78, memory: 85, connections: 1450 } },
|
|
11
|
+
{ id: 'db', label: 'Database', type: 'database', status: 'healthy', x: 400, y: 400, metrics: { cpu: 35, memory: 72, connections: 450 } },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
const simpleTopologyEdges: NetworkEdge[] = [
|
|
15
|
+
{ source: 'lb', target: 'web1', latency: 2, traffic: 'medium', status: 'active' },
|
|
16
|
+
{ source: 'lb', target: 'web2', latency: 3, traffic: 'high', status: 'active' },
|
|
17
|
+
{ source: 'lb', target: 'web3', latency: 5, traffic: 'medium', status: 'degraded' },
|
|
18
|
+
{ source: 'web1', target: 'db', latency: 8, traffic: 'medium', status: 'active' },
|
|
19
|
+
{ source: 'web2', target: 'db', latency: 7, traffic: 'high', status: 'active' },
|
|
20
|
+
{ source: 'web3', target: 'db', latency: 12, traffic: 'low', status: 'active' },
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
const complexTopologyNodes: NetworkNode[] = [
|
|
24
|
+
// Edge layer
|
|
25
|
+
{ id: 'fw1', label: 'Firewall', type: 'firewall', status: 'healthy', x: 400, y: 50 },
|
|
26
|
+
|
|
27
|
+
// Load balancers
|
|
28
|
+
{ id: 'lb1', label: 'LB Primary', type: 'loadbalancer', status: 'healthy', x: 250, y: 150 },
|
|
29
|
+
{ id: 'lb2', label: 'LB Secondary', type: 'loadbalancer', status: 'healthy', x: 550, y: 150 },
|
|
30
|
+
|
|
31
|
+
// Application servers
|
|
32
|
+
{ id: 'app1', label: 'App Server 1', type: 'server', status: 'healthy', x: 150, y: 280, metrics: { cpu: 42, memory: 58 } },
|
|
33
|
+
{ id: 'app2', label: 'App Server 2', type: 'server', status: 'healthy', x: 350, y: 280, metrics: { cpu: 38, memory: 52 } },
|
|
34
|
+
{ id: 'app3', label: 'App Server 3', type: 'server', status: 'warning', x: 550, y: 280, metrics: { cpu: 85, memory: 78 } },
|
|
35
|
+
{ id: 'app4', label: 'App Server 4', type: 'server', status: 'error', x: 750, y: 280, metrics: { cpu: 95, memory: 92 } },
|
|
36
|
+
|
|
37
|
+
// Cache layer
|
|
38
|
+
{ id: 'cache1', label: 'Redis Primary', type: 'database', status: 'healthy', x: 250, y: 400, metrics: { memory: 45 } },
|
|
39
|
+
{ id: 'cache2', label: 'Redis Replica', type: 'database', status: 'healthy', x: 550, y: 400, metrics: { memory: 42 } },
|
|
40
|
+
|
|
41
|
+
// Database cluster
|
|
42
|
+
{ id: 'db-primary', label: 'DB Primary', type: 'database', status: 'healthy', x: 300, y: 520, metrics: { cpu: 55, memory: 68 } },
|
|
43
|
+
{ id: 'db-replica1', label: 'DB Replica 1', type: 'database', status: 'healthy', x: 500, y: 520, metrics: { cpu: 32, memory: 54 } },
|
|
44
|
+
{ id: 'db-replica2', label: 'DB Replica 2', type: 'database', status: 'offline', x: 700, y: 520 },
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
const complexTopologyEdges: NetworkEdge[] = [
|
|
48
|
+
// Firewall to LBs
|
|
49
|
+
{ source: 'fw1', target: 'lb1', latency: 1, traffic: 'high', status: 'active' },
|
|
50
|
+
{ source: 'fw1', target: 'lb2', latency: 1, traffic: 'medium', status: 'active' },
|
|
51
|
+
|
|
52
|
+
// LBs to App servers
|
|
53
|
+
{ source: 'lb1', target: 'app1', latency: 2, traffic: 'high', status: 'active' },
|
|
54
|
+
{ source: 'lb1', target: 'app2', latency: 2, traffic: 'medium', status: 'active' },
|
|
55
|
+
{ source: 'lb2', target: 'app3', latency: 3, traffic: 'medium', status: 'degraded' },
|
|
56
|
+
{ source: 'lb2', target: 'app4', latency: 8, traffic: 'low', status: 'down' },
|
|
57
|
+
|
|
58
|
+
// App servers to cache
|
|
59
|
+
{ source: 'app1', target: 'cache1', latency: 1, traffic: 'high', status: 'active' },
|
|
60
|
+
{ source: 'app2', target: 'cache1', latency: 1, traffic: 'medium', status: 'active' },
|
|
61
|
+
{ source: 'app3', target: 'cache2', latency: 2, traffic: 'medium', status: 'active' },
|
|
62
|
+
{ source: 'app4', target: 'cache2', latency: 5, traffic: 'low', status: 'degraded' },
|
|
63
|
+
|
|
64
|
+
// Cache to cache (replication)
|
|
65
|
+
{ source: 'cache1', target: 'cache2', latency: 1, traffic: 'low', status: 'active' },
|
|
66
|
+
|
|
67
|
+
// Cache to DB
|
|
68
|
+
{ source: 'cache1', target: 'db-primary', latency: 3, traffic: 'medium', status: 'active' },
|
|
69
|
+
{ source: 'cache2', target: 'db-primary', latency: 4, traffic: 'low', status: 'active' },
|
|
70
|
+
|
|
71
|
+
// DB replication
|
|
72
|
+
{ source: 'db-primary', target: 'db-replica1', latency: 5, traffic: 'medium', status: 'active' },
|
|
73
|
+
{ source: 'db-primary', target: 'db-replica2', latency: 0, traffic: 'low', status: 'down' },
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
const cloudTopologyNodes: NetworkNode[] = [
|
|
77
|
+
{ id: 'internet', label: 'Internet', type: 'cloud', status: 'healthy', x: 400, y: 50 },
|
|
78
|
+
{ id: 'cdn', label: 'CDN Edge', type: 'router', status: 'healthy', x: 200, y: 150 },
|
|
79
|
+
{ id: 'waf', label: 'WAF', type: 'firewall', status: 'healthy', x: 400, y: 150 },
|
|
80
|
+
{ id: 'api-gw', label: 'API Gateway', type: 'router', status: 'healthy', x: 600, y: 150 },
|
|
81
|
+
{ id: 'k8s', label: 'K8s Cluster', type: 'cloud', status: 'healthy', x: 400, y: 280 },
|
|
82
|
+
{ id: 'rds', label: 'RDS', type: 'database', status: 'healthy', x: 250, y: 400 },
|
|
83
|
+
{ id: 'elasticache', label: 'ElastiCache', type: 'database', status: 'healthy', x: 550, y: 400 },
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
const cloudTopologyEdges: NetworkEdge[] = [
|
|
87
|
+
{ source: 'internet', target: 'cdn', traffic: 'high', status: 'active' },
|
|
88
|
+
{ source: 'internet', target: 'waf', traffic: 'high', status: 'active' },
|
|
89
|
+
{ source: 'internet', target: 'api-gw', traffic: 'medium', status: 'active' },
|
|
90
|
+
{ source: 'cdn', target: 'k8s', latency: 5, traffic: 'high', status: 'active' },
|
|
91
|
+
{ source: 'waf', target: 'k8s', latency: 3, traffic: 'high', status: 'active' },
|
|
92
|
+
{ source: 'api-gw', target: 'k8s', latency: 2, traffic: 'medium', status: 'active' },
|
|
93
|
+
{ source: 'k8s', target: 'rds', latency: 4, traffic: 'medium', status: 'active' },
|
|
94
|
+
{ source: 'k8s', target: 'elasticache', latency: 1, traffic: 'high', status: 'active' },
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
const meta: Meta<typeof WakaNetworkTopology> = {
|
|
98
|
+
title: 'Components/Charts/WakaNetworkTopology',
|
|
99
|
+
component: WakaNetworkTopology,
|
|
100
|
+
parameters: {
|
|
101
|
+
layout: 'centered',
|
|
102
|
+
docs: {
|
|
103
|
+
description: {
|
|
104
|
+
component: 'A network topology visualization for displaying infrastructure, showing nodes with status indicators, connections with latency/traffic metrics, and interactive features like zoom, pan, and selection.',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
tags: ['autodocs'],
|
|
109
|
+
argTypes: {
|
|
110
|
+
showLatency: {
|
|
111
|
+
control: 'boolean',
|
|
112
|
+
description: 'Show latency labels on edges',
|
|
113
|
+
},
|
|
114
|
+
showTraffic: {
|
|
115
|
+
control: 'boolean',
|
|
116
|
+
description: 'Show traffic intensity on edges',
|
|
117
|
+
},
|
|
118
|
+
animated: {
|
|
119
|
+
control: 'boolean',
|
|
120
|
+
description: 'Enable animations',
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default meta
|
|
126
|
+
type Story = StoryObj<typeof WakaNetworkTopology>
|
|
127
|
+
|
|
128
|
+
export const Default: Story = {
|
|
129
|
+
args: {
|
|
130
|
+
nodes: simpleTopologyNodes,
|
|
131
|
+
edges: simpleTopologyEdges,
|
|
132
|
+
},
|
|
133
|
+
render: (args) => (
|
|
134
|
+
<div className="w-[800px] h-[500px]">
|
|
135
|
+
<WakaNetworkTopology {...args} />
|
|
136
|
+
</div>
|
|
137
|
+
),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const ComplexInfrastructure: Story = {
|
|
141
|
+
render: () => (
|
|
142
|
+
<div className="w-[900px] h-[650px]">
|
|
143
|
+
<WakaNetworkTopology
|
|
144
|
+
nodes={complexTopologyNodes}
|
|
145
|
+
edges={complexTopologyEdges}
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
),
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const CloudArchitecture: Story = {
|
|
152
|
+
render: () => (
|
|
153
|
+
<div className="w-[800px] h-[500px]">
|
|
154
|
+
<WakaNetworkTopology
|
|
155
|
+
nodes={cloudTopologyNodes}
|
|
156
|
+
edges={cloudTopologyEdges}
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
),
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const NoLatency: Story = {
|
|
163
|
+
render: () => (
|
|
164
|
+
<div className="w-[800px] h-[500px]">
|
|
165
|
+
<WakaNetworkTopology
|
|
166
|
+
nodes={simpleTopologyNodes}
|
|
167
|
+
edges={simpleTopologyEdges}
|
|
168
|
+
showLatency={false}
|
|
169
|
+
/>
|
|
170
|
+
</div>
|
|
171
|
+
),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export const NoTraffic: Story = {
|
|
175
|
+
render: () => (
|
|
176
|
+
<div className="w-[800px] h-[500px]">
|
|
177
|
+
<WakaNetworkTopology
|
|
178
|
+
nodes={simpleTopologyNodes}
|
|
179
|
+
edges={simpleTopologyEdges}
|
|
180
|
+
showTraffic={false}
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
),
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export const NoAnimation: Story = {
|
|
187
|
+
render: () => (
|
|
188
|
+
<div className="w-[800px] h-[500px]">
|
|
189
|
+
<WakaNetworkTopology
|
|
190
|
+
nodes={simpleTopologyNodes}
|
|
191
|
+
edges={simpleTopologyEdges}
|
|
192
|
+
animated={false}
|
|
193
|
+
/>
|
|
194
|
+
</div>
|
|
195
|
+
),
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export const Interactive: Story = {
|
|
199
|
+
render: () => {
|
|
200
|
+
const [selectedNode, setSelectedNode] = React.useState<string | undefined>()
|
|
201
|
+
const [selectedEdge, setSelectedEdge] = React.useState<{ source: string; target: string } | null>(null)
|
|
202
|
+
|
|
203
|
+
const selectedNodeData = simpleTopologyNodes.find((n) => n.id === selectedNode)
|
|
204
|
+
const selectedEdgeData = selectedEdge
|
|
205
|
+
? simpleTopologyEdges.find((e) => e.source === selectedEdge.source && e.target === selectedEdge.target)
|
|
206
|
+
: null
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<div className="space-y-4">
|
|
210
|
+
<div className="w-[800px] h-[450px]">
|
|
211
|
+
<WakaNetworkTopology
|
|
212
|
+
nodes={simpleTopologyNodes}
|
|
213
|
+
edges={simpleTopologyEdges}
|
|
214
|
+
selectedNode={selectedNode}
|
|
215
|
+
onNodeClick={(nodeId) => {
|
|
216
|
+
setSelectedNode(selectedNode === nodeId ? undefined : nodeId)
|
|
217
|
+
setSelectedEdge(null)
|
|
218
|
+
}}
|
|
219
|
+
onEdgeClick={(source, target) => {
|
|
220
|
+
setSelectedEdge({ source, target })
|
|
221
|
+
setSelectedNode(undefined)
|
|
222
|
+
}}
|
|
223
|
+
/>
|
|
224
|
+
</div>
|
|
225
|
+
{(selectedNodeData || selectedEdgeData) && (
|
|
226
|
+
<div className="p-4 border rounded-lg bg-muted/50">
|
|
227
|
+
{selectedNodeData && (
|
|
228
|
+
<div>
|
|
229
|
+
<h4 className="font-semibold">{selectedNodeData.label}</h4>
|
|
230
|
+
<p className="text-sm text-muted-foreground">
|
|
231
|
+
Type: {selectedNodeData.type} | Status: {selectedNodeData.status}
|
|
232
|
+
</p>
|
|
233
|
+
{selectedNodeData.metrics && (
|
|
234
|
+
<div className="mt-2 grid grid-cols-3 gap-4 text-sm">
|
|
235
|
+
{selectedNodeData.metrics.cpu !== undefined && (
|
|
236
|
+
<div>
|
|
237
|
+
<span className="text-muted-foreground">CPU:</span> {selectedNodeData.metrics.cpu}%
|
|
238
|
+
</div>
|
|
239
|
+
)}
|
|
240
|
+
{selectedNodeData.metrics.memory !== undefined && (
|
|
241
|
+
<div>
|
|
242
|
+
<span className="text-muted-foreground">Memory:</span> {selectedNodeData.metrics.memory}%
|
|
243
|
+
</div>
|
|
244
|
+
)}
|
|
245
|
+
{selectedNodeData.metrics.connections !== undefined && (
|
|
246
|
+
<div>
|
|
247
|
+
<span className="text-muted-foreground">Connections:</span> {selectedNodeData.metrics.connections}
|
|
248
|
+
</div>
|
|
249
|
+
)}
|
|
250
|
+
</div>
|
|
251
|
+
)}
|
|
252
|
+
</div>
|
|
253
|
+
)}
|
|
254
|
+
{selectedEdgeData && (
|
|
255
|
+
<div>
|
|
256
|
+
<h4 className="font-semibold">
|
|
257
|
+
{selectedEdge?.source} → {selectedEdge?.target}
|
|
258
|
+
</h4>
|
|
259
|
+
<div className="mt-2 grid grid-cols-3 gap-4 text-sm">
|
|
260
|
+
<div>
|
|
261
|
+
<span className="text-muted-foreground">Latency:</span> {selectedEdgeData.latency}ms
|
|
262
|
+
</div>
|
|
263
|
+
<div>
|
|
264
|
+
<span className="text-muted-foreground">Traffic:</span> {selectedEdgeData.traffic}
|
|
265
|
+
</div>
|
|
266
|
+
<div>
|
|
267
|
+
<span className="text-muted-foreground">Status:</span> {selectedEdgeData.status}
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
)}
|
|
272
|
+
</div>
|
|
273
|
+
)}
|
|
274
|
+
</div>
|
|
275
|
+
)
|
|
276
|
+
},
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export const InfrastructureDashboard: Story = {
|
|
280
|
+
render: () => (
|
|
281
|
+
<div className="w-[950px] p-6 border rounded-lg">
|
|
282
|
+
<div className="flex justify-between items-start mb-4">
|
|
283
|
+
<div>
|
|
284
|
+
<h3 className="text-xl font-semibold">Production Infrastructure</h3>
|
|
285
|
+
<p className="text-sm text-muted-foreground">Real-time topology overview</p>
|
|
286
|
+
</div>
|
|
287
|
+
<div className="flex items-center gap-4 text-sm">
|
|
288
|
+
<div className="flex items-center gap-1.5">
|
|
289
|
+
<div className="w-2 h-2 rounded-full bg-green-500" />
|
|
290
|
+
<span>Healthy: 8</span>
|
|
291
|
+
</div>
|
|
292
|
+
<div className="flex items-center gap-1.5">
|
|
293
|
+
<div className="w-2 h-2 rounded-full bg-yellow-500" />
|
|
294
|
+
<span>Warning: 2</span>
|
|
295
|
+
</div>
|
|
296
|
+
<div className="flex items-center gap-1.5">
|
|
297
|
+
<div className="w-2 h-2 rounded-full bg-red-500" />
|
|
298
|
+
<span>Error: 1</span>
|
|
299
|
+
</div>
|
|
300
|
+
<div className="flex items-center gap-1.5">
|
|
301
|
+
<div className="w-2 h-2 rounded-full bg-gray-500" />
|
|
302
|
+
<span>Offline: 1</span>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
<div className="h-[550px]">
|
|
307
|
+
<WakaNetworkTopology
|
|
308
|
+
nodes={complexTopologyNodes}
|
|
309
|
+
edges={complexTopologyEdges}
|
|
310
|
+
/>
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
),
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export const NodeTypes: Story = {
|
|
317
|
+
render: () => {
|
|
318
|
+
const typeShowcaseNodes: NetworkNode[] = [
|
|
319
|
+
{ id: 'server', label: 'Server', type: 'server', status: 'healthy', x: 100, y: 100 },
|
|
320
|
+
{ id: 'database', label: 'Database', type: 'database', status: 'healthy', x: 250, y: 100 },
|
|
321
|
+
{ id: 'loadbalancer', label: 'Load Balancer', type: 'loadbalancer', status: 'healthy', x: 400, y: 100 },
|
|
322
|
+
{ id: 'firewall', label: 'Firewall', type: 'firewall', status: 'healthy', x: 550, y: 100 },
|
|
323
|
+
{ id: 'cloud', label: 'Cloud', type: 'cloud', status: 'healthy', x: 175, y: 250 },
|
|
324
|
+
{ id: 'client', label: 'Client', type: 'client', status: 'healthy', x: 325, y: 250 },
|
|
325
|
+
{ id: 'router', label: 'Router', type: 'router', status: 'healthy', x: 475, y: 250 },
|
|
326
|
+
]
|
|
327
|
+
|
|
328
|
+
return (
|
|
329
|
+
<div className="space-y-4">
|
|
330
|
+
<p className="text-sm text-muted-foreground">Available node types:</p>
|
|
331
|
+
<div className="w-[700px] h-[350px]">
|
|
332
|
+
<WakaNetworkTopology
|
|
333
|
+
nodes={typeShowcaseNodes}
|
|
334
|
+
edges={[]}
|
|
335
|
+
animated={false}
|
|
336
|
+
/>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
)
|
|
340
|
+
},
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export const StatusShowcase: Story = {
|
|
344
|
+
render: () => {
|
|
345
|
+
const statusNodes: NetworkNode[] = [
|
|
346
|
+
{ id: 'healthy', label: 'Healthy', type: 'server', status: 'healthy', x: 150, y: 150 },
|
|
347
|
+
{ id: 'warning', label: 'Warning', type: 'server', status: 'warning', x: 350, y: 150 },
|
|
348
|
+
{ id: 'error', label: 'Error', type: 'server', status: 'error', x: 550, y: 150 },
|
|
349
|
+
{ id: 'offline', label: 'Offline', type: 'server', status: 'offline', x: 350, y: 300 },
|
|
350
|
+
]
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<div className="space-y-4">
|
|
354
|
+
<p className="text-sm text-muted-foreground">Node status indicators:</p>
|
|
355
|
+
<div className="w-[700px] h-[400px]">
|
|
356
|
+
<WakaNetworkTopology
|
|
357
|
+
nodes={statusNodes}
|
|
358
|
+
edges={[]}
|
|
359
|
+
/>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
)
|
|
363
|
+
},
|
|
364
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { WakaNotifications, useNotifications } from './index'
|
|
3
|
+
import type { Notification } from './index'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
import { CheckCircle, AlertTriangle, XCircle, Info, MessageSquare, UserPlus, ShoppingCart, CreditCard } from 'lucide-react'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof WakaNotifications> = {
|
|
8
|
+
title: 'Components/Display/WakaNotifications',
|
|
9
|
+
component: WakaNotifications,
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'centered',
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: 'A notification center component with popover, tabs, and action buttons for managing notifications.',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
tags: ['autodocs'],
|
|
19
|
+
argTypes: {
|
|
20
|
+
showBadge: {
|
|
21
|
+
control: 'boolean',
|
|
22
|
+
description: 'Show unread count badge',
|
|
23
|
+
},
|
|
24
|
+
showTabs: {
|
|
25
|
+
control: 'boolean',
|
|
26
|
+
description: 'Show All/Unread tabs',
|
|
27
|
+
},
|
|
28
|
+
maxHeight: {
|
|
29
|
+
control: 'text',
|
|
30
|
+
description: 'Maximum height of notification list',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default meta
|
|
36
|
+
type Story = StoryObj<typeof WakaNotifications>
|
|
37
|
+
|
|
38
|
+
const sampleNotifications: Notification[] = [
|
|
39
|
+
{
|
|
40
|
+
id: '1',
|
|
41
|
+
title: 'New comment on your post',
|
|
42
|
+
message: 'John Doe commented on your recent post about React patterns.',
|
|
43
|
+
type: 'info',
|
|
44
|
+
read: false,
|
|
45
|
+
timestamp: new Date(Date.now() - 5 * 60 * 1000), // 5 mins ago
|
|
46
|
+
icon: <MessageSquare className="h-5 w-5" />,
|
|
47
|
+
action: {
|
|
48
|
+
label: 'View comment',
|
|
49
|
+
onClick: () => console.log('View comment clicked'),
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: '2',
|
|
54
|
+
title: 'Payment successful',
|
|
55
|
+
message: 'Your payment of $99.00 has been processed successfully.',
|
|
56
|
+
type: 'success',
|
|
57
|
+
read: false,
|
|
58
|
+
timestamp: new Date(Date.now() - 30 * 60 * 1000), // 30 mins ago
|
|
59
|
+
icon: <CreditCard className="h-5 w-5" />,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: '3',
|
|
63
|
+
title: 'New follower',
|
|
64
|
+
message: 'Sarah Miller started following you.',
|
|
65
|
+
type: 'info',
|
|
66
|
+
read: true,
|
|
67
|
+
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000), // 2 hours ago
|
|
68
|
+
icon: <UserPlus className="h-5 w-5" />,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: '4',
|
|
72
|
+
title: 'Low storage warning',
|
|
73
|
+
message: 'You are running low on storage space. Consider upgrading your plan.',
|
|
74
|
+
type: 'warning',
|
|
75
|
+
read: false,
|
|
76
|
+
timestamp: new Date(Date.now() - 5 * 60 * 60 * 1000), // 5 hours ago
|
|
77
|
+
icon: <AlertTriangle className="h-5 w-5" />,
|
|
78
|
+
action: {
|
|
79
|
+
label: 'Upgrade now',
|
|
80
|
+
onClick: () => console.log('Upgrade clicked'),
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: '5',
|
|
85
|
+
title: 'Build failed',
|
|
86
|
+
message: 'Your latest deployment to production failed. Check the logs for details.',
|
|
87
|
+
type: 'error',
|
|
88
|
+
read: true,
|
|
89
|
+
timestamp: new Date(Date.now() - 24 * 60 * 60 * 1000), // 1 day ago
|
|
90
|
+
icon: <XCircle className="h-5 w-5" />,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: '6',
|
|
94
|
+
title: 'Order shipped',
|
|
95
|
+
message: 'Your order #12345 has been shipped and is on its way.',
|
|
96
|
+
type: 'success',
|
|
97
|
+
read: true,
|
|
98
|
+
timestamp: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000), // 2 days ago
|
|
99
|
+
icon: <ShoppingCart className="h-5 w-5" />,
|
|
100
|
+
},
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
export const Default: Story = {
|
|
104
|
+
args: {},
|
|
105
|
+
render: (args) => {
|
|
106
|
+
const {
|
|
107
|
+
notifications,
|
|
108
|
+
markAsRead,
|
|
109
|
+
markAllAsRead,
|
|
110
|
+
deleteNotification,
|
|
111
|
+
deleteAllNotifications,
|
|
112
|
+
} = useNotifications(sampleNotifications)
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<div className="p-8">
|
|
116
|
+
<WakaNotifications
|
|
117
|
+
notifications={notifications}
|
|
118
|
+
onMarkAsRead={markAsRead}
|
|
119
|
+
onMarkAllAsRead={markAllAsRead}
|
|
120
|
+
onDelete={deleteNotification}
|
|
121
|
+
onDeleteAll={deleteAllNotifications}
|
|
122
|
+
onSettingsClick={() => console.log('Settings clicked')}
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
)
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const Empty: Story = {
|
|
130
|
+
render: () => (
|
|
131
|
+
<div className="p-8">
|
|
132
|
+
<WakaNotifications
|
|
133
|
+
notifications={[]}
|
|
134
|
+
emptyMessage="No notifications yet"
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const WithoutTabs: Story = {
|
|
141
|
+
render: () => {
|
|
142
|
+
const {
|
|
143
|
+
notifications,
|
|
144
|
+
markAsRead,
|
|
145
|
+
deleteNotification,
|
|
146
|
+
} = useNotifications(sampleNotifications)
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div className="p-8">
|
|
150
|
+
<WakaNotifications
|
|
151
|
+
notifications={notifications}
|
|
152
|
+
onMarkAsRead={markAsRead}
|
|
153
|
+
onDelete={deleteNotification}
|
|
154
|
+
showTabs={false}
|
|
155
|
+
/>
|
|
156
|
+
</div>
|
|
157
|
+
)
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const WithoutBadge: Story = {
|
|
162
|
+
render: () => {
|
|
163
|
+
const { notifications } = useNotifications(sampleNotifications)
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div className="p-8">
|
|
167
|
+
<WakaNotifications
|
|
168
|
+
notifications={notifications}
|
|
169
|
+
showBadge={false}
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
)
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export const Interactive: Story = {
|
|
177
|
+
render: () => {
|
|
178
|
+
const {
|
|
179
|
+
notifications,
|
|
180
|
+
addNotification,
|
|
181
|
+
markAsRead,
|
|
182
|
+
markAllAsRead,
|
|
183
|
+
deleteNotification,
|
|
184
|
+
deleteAllNotifications,
|
|
185
|
+
unreadCount,
|
|
186
|
+
} = useNotifications([])
|
|
187
|
+
|
|
188
|
+
const addRandomNotification = () => {
|
|
189
|
+
const types: Notification['type'][] = ['info', 'success', 'warning', 'error']
|
|
190
|
+
const randomType = types[Math.floor(Math.random() * types.length)]
|
|
191
|
+
const icons = {
|
|
192
|
+
info: <Info className="h-5 w-5" />,
|
|
193
|
+
success: <CheckCircle className="h-5 w-5" />,
|
|
194
|
+
warning: <AlertTriangle className="h-5 w-5" />,
|
|
195
|
+
error: <XCircle className="h-5 w-5" />,
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
addNotification({
|
|
199
|
+
title: `New ${randomType} notification`,
|
|
200
|
+
message: `This is a sample ${randomType} notification message.`,
|
|
201
|
+
type: randomType,
|
|
202
|
+
icon: icons[randomType],
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<div className="flex flex-col items-center gap-4 p-8">
|
|
208
|
+
<div className="flex items-center gap-4">
|
|
209
|
+
<WakaNotifications
|
|
210
|
+
notifications={notifications}
|
|
211
|
+
onMarkAsRead={markAsRead}
|
|
212
|
+
onMarkAllAsRead={markAllAsRead}
|
|
213
|
+
onDelete={deleteNotification}
|
|
214
|
+
onDeleteAll={deleteAllNotifications}
|
|
215
|
+
/>
|
|
216
|
+
<span className="text-sm text-muted-foreground">
|
|
217
|
+
{unreadCount} unread
|
|
218
|
+
</span>
|
|
219
|
+
</div>
|
|
220
|
+
<button
|
|
221
|
+
onClick={addRandomNotification}
|
|
222
|
+
className="px-4 py-2 bg-primary text-primary-foreground rounded-md text-sm"
|
|
223
|
+
>
|
|
224
|
+
Add Random Notification
|
|
225
|
+
</button>
|
|
226
|
+
</div>
|
|
227
|
+
)
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export const ManyNotifications: Story = {
|
|
232
|
+
render: () => {
|
|
233
|
+
const manyNotifications: Notification[] = Array.from({ length: 20 }, (_, i) => ({
|
|
234
|
+
id: `notif-${i}`,
|
|
235
|
+
title: `Notification ${i + 1}`,
|
|
236
|
+
message: `This is notification number ${i + 1} with some sample content.`,
|
|
237
|
+
type: (['info', 'success', 'warning', 'error'] as const)[i % 4],
|
|
238
|
+
read: i > 5,
|
|
239
|
+
timestamp: new Date(Date.now() - i * 30 * 60 * 1000),
|
|
240
|
+
}))
|
|
241
|
+
|
|
242
|
+
const {
|
|
243
|
+
notifications,
|
|
244
|
+
markAsRead,
|
|
245
|
+
markAllAsRead,
|
|
246
|
+
deleteNotification,
|
|
247
|
+
} = useNotifications(manyNotifications)
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<div className="p-8">
|
|
251
|
+
<WakaNotifications
|
|
252
|
+
notifications={notifications}
|
|
253
|
+
onMarkAsRead={markAsRead}
|
|
254
|
+
onMarkAllAsRead={markAllAsRead}
|
|
255
|
+
onDelete={deleteNotification}
|
|
256
|
+
maxHeight="350px"
|
|
257
|
+
/>
|
|
258
|
+
</div>
|
|
259
|
+
)
|
|
260
|
+
},
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export const HeaderIntegration: Story = {
|
|
264
|
+
render: () => {
|
|
265
|
+
const { notifications, markAsRead, markAllAsRead, deleteNotification } = useNotifications(sampleNotifications)
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<div className="w-[600px] border rounded-lg">
|
|
269
|
+
<header className="flex items-center justify-between px-4 py-3 border-b">
|
|
270
|
+
<div className="font-semibold">My Application</div>
|
|
271
|
+
<div className="flex items-center gap-2">
|
|
272
|
+
<WakaNotifications
|
|
273
|
+
notifications={notifications}
|
|
274
|
+
onMarkAsRead={markAsRead}
|
|
275
|
+
onMarkAllAsRead={markAllAsRead}
|
|
276
|
+
onDelete={deleteNotification}
|
|
277
|
+
onSettingsClick={() => console.log('Settings')}
|
|
278
|
+
/>
|
|
279
|
+
<div className="h-8 w-8 rounded-full bg-muted flex items-center justify-center text-sm">
|
|
280
|
+
JD
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</header>
|
|
284
|
+
<div className="p-8 text-center text-muted-foreground">
|
|
285
|
+
Main content area
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
)
|
|
289
|
+
},
|
|
290
|
+
}
|