diff --git a/frontends/nextjs/src/components/misc/demos/DBALDemo.tsx b/frontends/nextjs/src/components/misc/demos/DBALDemo.tsx
index af4b1dcea..0292b4142 100644
--- a/frontends/nextjs/src/components/misc/demos/DBALDemo.tsx
+++ b/frontends/nextjs/src/components/misc/demos/DBALDemo.tsx
@@ -1,23 +1,25 @@
/**
* DBAL Demo Component
- *
+ *
* Demonstrates the integration of the DBAL TypeScript client
* with the MetaBuilder application.
*/
-import { useState } from 'react'
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
-import { Button } from '@/components/ui'
-import { Input } from '@/components/ui'
-import { Label } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
-import { Badge } from '@/components/ui'
-import { toast } from 'sonner'
-import { useKVStore, useBlobStorage, useCachedData } from '@/hooks/useDBAL'
+import { BlobStorageDemo } from './dbal/BlobStorageDemo'
+import { CachedDataDemo } from './dbal/CachedDataDemo'
+import { KVStoreDemo } from './dbal/KVStoreDemo'
+import { DBALTabConfig, DBAL_CONTAINER_CLASS, DBAL_TAB_GRID_CLASS } from './dbal/dbal-demo.utils'
+
+const tabs: DBALTabConfig[] = [
+ { value: 'kv', label: 'Key-Value Store', content: },
+ { value: 'blob', label: 'Blob Storage', content: },
+ { value: 'cache', label: 'Cached Data', content: },
+]
export function DBALDemo() {
return (
-
+
DBAL Integration Demo
@@ -25,404 +27,21 @@ export function DBALDemo() {
-
-
- Key-Value Store
- Blob Storage
- Cached Data
+
+
+ {tabs.map((tab) => (
+
+ {tab.label}
+
+ ))}
-
-
-
-
-
-
-
-
-
-
-
+ {tabs.map((tab) => (
+
+ {tab.content}
+
+ ))}
)
}
-
-function KVStoreDemo() {
- const kv = useKVStore()
- const [key, setKey] = useState('demo-key')
- const [value, setValue] = useState('Hello, DBAL!')
- const [ttl, setTtl] = useState
(undefined)
- const [retrievedValue, setRetrievedValue] = useState(null)
- const [listKey, setListKey] = useState('demo-list')
- const [listItems, setListItems] = useState([])
-
- const handleSet = async () => {
- try {
- await kv.set(key, value, ttl)
- toast.success(`Stored: ${key} = ${value}`)
- } catch (error) {
- console.error('KV Set Error:', error)
- }
- }
-
- const handleGet = async () => {
- try {
- const result = await kv.get(key)
- setRetrievedValue(result)
- if (result) {
- toast.success(`Retrieved: ${result}`)
- } else {
- toast.info('Key not found')
- }
- } catch (error) {
- console.error('KV Get Error:', error)
- }
- }
-
- const handleDelete = async () => {
- try {
- const deleted = await kv.delete(key)
- if (deleted) {
- toast.success(`Deleted: ${key}`)
- setRetrievedValue(null)
- } else {
- toast.info('Key not found')
- }
- } catch (error) {
- console.error('KV Delete Error:', error)
- }
- }
-
- const handleListAdd = async () => {
- try {
- await kv.listAdd(listKey, ['Item 1', 'Item 2', 'Item 3'])
- toast.success('Added items to list')
- handleListGet()
- } catch (error) {
- console.error('List Add Error:', error)
- }
- }
-
- const handleListGet = async () => {
- try {
- const items = await kv.listGet(listKey)
- setListItems(items)
- toast.success(`Retrieved ${items.length} items`)
- } catch (error) {
- console.error('List Get Error:', error)
- }
- }
-
- return (
-
-
-
- Key-Value Operations
- Store and retrieve simple key-value data
-
-
-
-
- setKey(e.target.value)}
- placeholder="my-key"
- />
-
-
-
- setValue(e.target.value)}
- placeholder="my-value"
- />
-
-
-
- setTtl(e.target.value ? parseInt(e.target.value) : undefined)}
- placeholder="3600"
- />
-
-
-
-
-
-
- {retrievedValue !== null && (
-
- )}
- {!kv.isReady && (
- Initializing DBAL...
- )}
-
-
-
-
-
- List Operations
- Store and retrieve lists of items
-
-
-
-
- setListKey(e.target.value)}
- placeholder="my-list"
- />
-
-
-
-
-
- {listItems.length > 0 && (
-
-
Items ({listItems.length}):
-
- {listItems.map((item, index) => (
-
- {item}
-
- ))}
-
-
- )}
-
-
-
- )
-}
-
-function BlobStorageDemo() {
- const blob = useBlobStorage()
- const [fileName, setFileName] = useState('demo.txt')
- const [fileContent, setFileContent] = useState('Hello from DBAL blob storage!')
- const [files, setFiles] = useState([])
- const [downloadedContent, setDownloadedContent] = useState(null)
-
- const handleUpload = async () => {
- try {
- const encoder = new TextEncoder()
- const data = encoder.encode(fileContent)
- await blob.upload(fileName, data, {
- 'content-type': 'text/plain',
- 'uploaded-at': new Date().toISOString(),
- })
- handleList()
- } catch (error) {
- console.error('Upload Error:', error)
- }
- }
-
- const handleDownload = async () => {
- try {
- const data = await blob.download(fileName)
- const decoder = new TextDecoder()
- const content = decoder.decode(data)
- setDownloadedContent(content)
- toast.success('Downloaded successfully')
- } catch (error) {
- console.error('Download Error:', error)
- }
- }
-
- const handleDelete = async () => {
- try {
- await blob.delete(fileName)
- setDownloadedContent(null)
- handleList()
- } catch (error) {
- console.error('Delete Error:', error)
- }
- }
-
- const handleList = async () => {
- try {
- const fileList = await blob.list()
- setFiles(fileList)
- toast.success(`Found ${fileList.length} files`)
- } catch (error) {
- console.error('List Error:', error)
- }
- }
-
- return (
-
-
- Blob Storage Operations
- Upload, download, and manage binary data
-
-
-
-
- setFileName(e.target.value)}
- placeholder="file.txt"
- />
-
-
-
-
-
-
-
-
-
-
- {downloadedContent && (
-
-
Downloaded Content:
-
-
- )}
- {files.length > 0 && (
-
-
Files ({files.length}):
-
- {files.map((file) => (
-
- {file}
-
- ))}
-
-
- )}
-
-
- )
-}
-
-function CachedDataDemo() {
- interface UserPreferences {
- theme: string
- language: string
- notifications: boolean
- }
-
- const { data, loading, error, save, clear, isReady } = useCachedData(
- 'user-preferences'
- )
-
- const [theme, setTheme] = useState('dark')
- const [language, setLanguage] = useState('en')
- const [notifications, setNotifications] = useState(true)
-
- const handleSave = async () => {
- try {
- await save({ theme, language, notifications }, 3600) // Cache for 1 hour
- toast.success('Preferences saved')
- } catch (error) {
- console.error('Save Error:', error)
- }
- }
-
- const handleClear = async () => {
- try {
- await clear()
- toast.success('Cache cleared')
- } catch (error) {
- console.error('Clear Error:', error)
- }
- }
-
- return (
-
-
- Cached Data Hook
- Automatic caching with React hooks
-
-
- {loading && Loading cached data...}
- {error && Error: {error}}
-
- {data && (
-
-
Cached Preferences:
-
Theme: {data.theme}
-
Language: {data.language}
-
Notifications: {data.notifications ? 'On' : 'Off'}
-
- )}
-
-
-
- setTheme(e.target.value)}
- placeholder="dark"
- />
-
-
-
-
- setLanguage(e.target.value)}
- placeholder="en"
- />
-
-
-
- setNotifications(e.target.checked)}
- className="w-4 h-4"
- />
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/frontends/nextjs/src/components/misc/demos/dbal/BlobStorageDemo.tsx b/frontends/nextjs/src/components/misc/demos/dbal/BlobStorageDemo.tsx
new file mode 100644
index 000000000..483c8e981
--- /dev/null
+++ b/frontends/nextjs/src/components/misc/demos/dbal/BlobStorageDemo.tsx
@@ -0,0 +1,124 @@
+import { useState } from 'react'
+import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Label } from '@/components/ui'
+import { useBlobStorage } from '@/hooks/useDBAL'
+import { renderInitializationBadge } from './dbal-demo.utils'
+import { toast } from 'sonner'
+
+export function BlobStorageDemo() {
+ const blob = useBlobStorage()
+ const [fileName, setFileName] = useState('demo.txt')
+ const [fileContent, setFileContent] = useState('Hello from DBAL blob storage!')
+ const [files, setFiles] = useState([])
+ const [downloadedContent, setDownloadedContent] = useState(null)
+
+ const handleUpload = async () => {
+ try {
+ const encoder = new TextEncoder()
+ const data = encoder.encode(fileContent)
+ await blob.upload(fileName, data, {
+ 'content-type': 'text/plain',
+ 'uploaded-at': new Date().toISOString(),
+ })
+ handleList()
+ } catch (error) {
+ console.error('Upload Error:', error)
+ }
+ }
+
+ const handleDownload = async () => {
+ try {
+ const data = await blob.download(fileName)
+ const decoder = new TextDecoder()
+ const content = decoder.decode(data)
+ setDownloadedContent(content)
+ toast.success('Downloaded successfully')
+ } catch (error) {
+ console.error('Download Error:', error)
+ }
+ }
+
+ const handleDelete = async () => {
+ try {
+ await blob.delete(fileName)
+ setDownloadedContent(null)
+ handleList()
+ } catch (error) {
+ console.error('Delete Error:', error)
+ }
+ }
+
+ const handleList = async () => {
+ try {
+ const fileList = await blob.list()
+ setFiles(fileList)
+ toast.success(`Found ${fileList.length} files`)
+ } catch (error) {
+ console.error('List Error:', error)
+ }
+ }
+
+ return (
+
+
+ Blob Storage Operations
+ Upload, download, and manage binary data
+
+
+
+
+ setFileName(e.target.value)}
+ placeholder="file.txt"
+ />
+
+
+
+
+
+
+
+
+
+
+ {downloadedContent && (
+
+
Downloaded Content:
+
+
+ )}
+ {files.length > 0 && (
+
+
Files ({files.length}):
+
+ {files.map((file) => (
+
+ {file}
+
+ ))}
+
+
+ )}
+ {renderInitializationBadge(blob.isReady, 'Initializing DBAL...')}
+
+
+ )
+}
diff --git a/frontends/nextjs/src/components/misc/demos/dbal/CachedDataDemo.tsx b/frontends/nextjs/src/components/misc/demos/dbal/CachedDataDemo.tsx
new file mode 100644
index 000000000..9b68a0d9c
--- /dev/null
+++ b/frontends/nextjs/src/components/misc/demos/dbal/CachedDataDemo.tsx
@@ -0,0 +1,87 @@
+import { useState } from 'react'
+import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Label } from '@/components/ui'
+import { useCachedData } from '@/hooks/useDBAL'
+import { toast } from 'sonner'
+
+interface UserPreferences {
+ theme: string
+ language: string
+ notifications: boolean
+}
+
+export function CachedDataDemo() {
+ const { data, loading, error, save, clear, isReady } = useCachedData('user-preferences')
+ const [theme, setTheme] = useState('dark')
+ const [language, setLanguage] = useState('en')
+ const [notifications, setNotifications] = useState(true)
+
+ const handleSave = async () => {
+ try {
+ await save({ theme, language, notifications }, 3600)
+ toast.success('Preferences saved')
+ } catch (error) {
+ console.error('Save Error:', error)
+ }
+ }
+
+ const handleClear = async () => {
+ try {
+ await clear()
+ toast.success('Cache cleared')
+ } catch (error) {
+ console.error('Clear Error:', error)
+ }
+ }
+
+ return (
+
+
+ Cached Data Hook
+ Automatic caching with React hooks
+
+
+ {loading && Loading cached data...}
+ {error && Error: {error}}
+
+ {data && (
+
+
Cached Preferences:
+
Theme: {data.theme}
+
Language: {data.language}
+
Notifications: {data.notifications ? 'On' : 'Off'}
+
+ )}
+
+
+
+ setTheme(e.target.value)} placeholder="dark" />
+
+
+
+
+ setLanguage(e.target.value)} placeholder="en" />
+
+
+
+ setNotifications(e.target.checked)}
+ className="w-4 h-4"
+ />
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/frontends/nextjs/src/components/misc/demos/dbal/KVStoreDemo.tsx b/frontends/nextjs/src/components/misc/demos/dbal/KVStoreDemo.tsx
new file mode 100644
index 000000000..079d241de
--- /dev/null
+++ b/frontends/nextjs/src/components/misc/demos/dbal/KVStoreDemo.tsx
@@ -0,0 +1,141 @@
+import { useState } from 'react'
+import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Label } from '@/components/ui'
+import { useKVStore } from '@/hooks/useDBAL'
+import { renderInitializationBadge } from './dbal-demo.utils'
+import { toast } from 'sonner'
+export function KVStoreDemo() {
+ const kv = useKVStore()
+ const [key, setKey] = useState('demo-key')
+ const [value, setValue] = useState('Hello, DBAL!')
+ const [ttl, setTtl] = useState(undefined)
+ const [retrievedValue, setRetrievedValue] = useState(null)
+ const [listKey, setListKey] = useState('demo-list')
+ const [listItems, setListItems] = useState([])
+ const handleSet = async () => {
+ try {
+ await kv.set(key, value, ttl)
+ toast.success(`Stored: ${key} = ${value}`)
+ } catch (error) {
+ console.error('KV Set Error:', error)
+ }
+ }
+ const handleGet = async () => {
+ try {
+ const result = await kv.get(key)
+ setRetrievedValue(result)
+ if (result) {
+ toast.success(`Retrieved: ${result}`)
+ } else {
+ toast.info('Key not found')
+ }
+ } catch (error) {
+ console.error('KV Get Error:', error)
+ }
+ }
+ const handleDelete = async () => {
+ try {
+ const deleted = await kv.delete(key)
+ if (deleted) {
+ toast.success(`Deleted: ${key}`)
+ setRetrievedValue(null)
+ } else {
+ toast.info('Key not found')
+ }
+ } catch (error) {
+ console.error('KV Delete Error:', error)
+ }
+ }
+ const handleListAdd = async () => {
+ try {
+ await kv.listAdd(listKey, ['Item 1', 'Item 2', 'Item 3'])
+ toast.success('Added items to list')
+ handleListGet()
+ } catch (error) {
+ console.error('List Add Error:', error)
+ }
+ }
+ const handleListGet = async () => {
+ try {
+ const items = await kv.listGet(listKey)
+ setListItems(items)
+ toast.success(`Retrieved ${items.length} items`)
+ } catch (error) {
+ console.error('List Get Error:', error)
+ }
+ }
+ return (
+
+
+
+ Key-Value Operations
+ Store and retrieve simple key-value data
+
+
+
+
+ setKey(e.target.value)} placeholder="my-key" />
+
+
+
+ setValue(e.target.value)} placeholder="my-value" />
+
+
+
+ setTtl(e.target.value ? parseInt(e.target.value) : undefined)}
+ placeholder="3600"
+ />
+
+
+
+
+
+
+ {retrievedValue !== null && (
+
+ )}
+ {renderInitializationBadge(kv.isReady, 'Initializing DBAL...')}
+
+
+
+
+
+ List Operations
+ Store and retrieve lists of items
+
+
+
+
+ setListKey(e.target.value)}
+ placeholder="my-list"
+ />
+
+
+
+
+
+ {listItems.length > 0 && (
+
+
Items ({listItems.length}):
+
+ {listItems.map((item, index) => (
+
+ {item}
+
+ ))}
+
+
+ )}
+
+
+
+ )
+}
diff --git a/frontends/nextjs/src/components/misc/demos/dbal/dbal-demo.utils.ts b/frontends/nextjs/src/components/misc/demos/dbal/dbal-demo.utils.ts
new file mode 100644
index 000000000..826792fc8
--- /dev/null
+++ b/frontends/nextjs/src/components/misc/demos/dbal/dbal-demo.utils.ts
@@ -0,0 +1,17 @@
+import type { ReactNode } from 'react'
+import { Badge } from '@/components/ui'
+
+export interface DBALTabConfig {
+ value: string
+ label: string
+ content: ReactNode
+}
+
+export const DBAL_CONTAINER_CLASS = 'container mx-auto p-6 max-w-6xl'
+export const DBAL_TAB_GRID_CLASS = 'grid w-full grid-cols-3'
+
+export function renderInitializationBadge(isReady: boolean, message: string) {
+ if (isReady) return null
+
+ return {message}
+}