mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
docs: nextjs,frontends,tsx (92 files)
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"build": "next build --webpack",
|
||||
"start": "next start",
|
||||
"kill": "fuser -k 3000/tcp",
|
||||
"typecheck": "tsc --noEmit",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Toaster } from '@/components/ui/sonner'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Button } from '@/components/ui'
|
||||
// Import level-specific UI components
|
||||
import { Level1 } from '@/components/Level1'
|
||||
import { Level2 } from '@/components/Level2'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { SecureDatabase, SecurityContext, AuditLog } from '@/lib/secure-db-layer'
|
||||
import { ShieldCheck, Clock, User, ChartLine, Warning } from '@phosphor-icons/react'
|
||||
import type { User as UserType } from '@/lib/level-types'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from 'react'
|
||||
import { useKV } from '@github/spark/hooks'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Button } from '@/components/ui'
|
||||
import { ComponentCatalog } from './ComponentCatalog'
|
||||
import { Canvas } from './Canvas'
|
||||
import { PropertyInspector } from './PropertyInspector'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
import Editor from '@monaco-editor/react'
|
||||
import { FloppyDisk, X, ShieldCheck, Warning } from '@phosphor-icons/react'
|
||||
import { securityScanner, type SecurityScanResult } from '@/lib/security-scanner'
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Textarea } from '@/components/ui'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import { Switch } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Database, ComponentNode, ComponentConfig } from '@/lib/database'
|
||||
import { componentCatalog } from '@/lib/component-catalog'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import {
|
||||
Tree,
|
||||
Plus,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Database } from '@/lib/database'
|
||||
import { Plus, X, FloppyDisk } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
||||
import { Card } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
|
||||
import { Database, CssCategory } from '@/lib/database'
|
||||
import { Plus, X, Pencil, Trash, FloppyDisk } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
*/
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
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'
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Database, DB_KEYS } from '@/lib/database'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Card } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import { Database } from '@/lib/database'
|
||||
import { Plus, X, FloppyDisk, Trash, Pencil } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Textarea } from '@/components/ui'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import { Switch } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import type { FieldSchema, SchemaConfig } from '@/lib/schema-types'
|
||||
import { getFieldLabel, getHelpText, findModel, getModelLabel } from '@/lib/schema-utils'
|
||||
import { useKV } from '@github/spark/hooks'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { RenderComponent } from '@/components/RenderComponent'
|
||||
import { SignOut, House, List, X } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { ArrowClockwise, Info, Warning } from '@phosphor-icons/react'
|
||||
import { useGitHubFetcher } from '@/hooks/useGitHubFetcher'
|
||||
import { useAutoRefresh } from '@/hooks/useAutoRefresh'
|
||||
import { WorkflowRunCard } from '@/components/WorkflowRunCard'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
|
||||
/**
|
||||
* Refactored GitHub Actions Fetcher Component
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { CheckCircle, XCircle, ArrowClockwise, ArrowSquareOut, Info, Warning, TrendUp, TrendDown, Robot, Download, FileText } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
import { Octokit } from 'octokit'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
|
||||
interface WorkflowRun {
|
||||
id: number
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
import { Database } from '@/lib/database'
|
||||
import { toast } from 'sonner'
|
||||
import { Clock, Key, WarningCircle, CheckCircle } from '@phosphor-icons/react'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { PaperPlaneTilt, Users, SignOut, Gear } from '@phosphor-icons/react'
|
||||
import { useKV } from '@github/spark/hooks'
|
||||
import type { User } from '@/lib/level-types'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { PaperPlaneTilt, Users, SignOut, Gear } from '@phosphor-icons/react'
|
||||
import { useKV } from '@github/spark/hooks'
|
||||
import type { User } from '@/lib/level-types'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
import { FloppyDisk, X, Warning, ShieldCheck } from '@phosphor-icons/react'
|
||||
import Editor from '@monaco-editor/react'
|
||||
import { securityScanner, type SecurityScanResult } from '@/lib/security-scanner'
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Database } from '@/lib/database'
|
||||
import { getScrambledPassword } from '@/lib/auth'
|
||||
import { NavigationBar } from './level1/NavigationBar'
|
||||
import { GodCredentialsBanner } from './level1/GodCredentialsBanner'
|
||||
@@ -25,7 +24,7 @@ import { HeroSection } from './level1/HeroSection'
|
||||
import { FeaturesSection } from './level1/FeaturesSection'
|
||||
import { ContactSection } from './level1/ContactSection'
|
||||
import { AppFooter } from './shared/AppFooter'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { GitHubActionsFetcher } from './GitHubActionsFetcher'
|
||||
|
||||
// Props for Level1 component
|
||||
@@ -58,42 +57,56 @@ export function Level1({ onNavigate }: Level1Props) {
|
||||
|
||||
// Initialize component state on mount
|
||||
useEffect(() => {
|
||||
let interval: ReturnType<typeof setInterval> | undefined
|
||||
|
||||
const checkCredentials = async () => {
|
||||
// Check if god credentials should be displayed
|
||||
const shouldShow = await Database.shouldShowGodCredentials()
|
||||
setShowGodCredentials(shouldShow)
|
||||
|
||||
// Get supergod account if exists
|
||||
const superGod = await Database.getSuperGod()
|
||||
const firstLoginFlags = await Database.getFirstLoginFlags()
|
||||
setShowSuperGodCredentials(superGod !== null && firstLoginFlags['supergod'] === true)
|
||||
|
||||
// Update timer for god credentials expiry
|
||||
if (shouldShow) {
|
||||
const expiry = await Database.getGodCredentialsExpiry()
|
||||
const updateTimer = () => {
|
||||
const now = Date.now()
|
||||
const diff = expiry - now
|
||||
|
||||
// Hide credentials when expired
|
||||
if (diff <= 0) {
|
||||
setShowGodCredentials(false)
|
||||
setTimeRemaining('')
|
||||
} else {
|
||||
try {
|
||||
const { Database } = await import('@/lib/database')
|
||||
|
||||
// Check if god credentials should be displayed
|
||||
const shouldShow = await Database.shouldShowGodCredentials()
|
||||
setShowGodCredentials(shouldShow)
|
||||
|
||||
// Get supergod account if exists
|
||||
const superGod = await Database.getSuperGod()
|
||||
const firstLoginFlags = await Database.getFirstLoginFlags()
|
||||
setShowSuperGodCredentials(superGod !== null && firstLoginFlags['supergod'] === true)
|
||||
|
||||
// Update timer for god credentials expiry
|
||||
if (shouldShow) {
|
||||
const expiry = await Database.getGodCredentialsExpiry()
|
||||
const updateTimer = () => {
|
||||
const now = Date.now()
|
||||
const diff = expiry - now
|
||||
|
||||
// Hide credentials when expired
|
||||
if (diff <= 0) {
|
||||
setShowGodCredentials(false)
|
||||
setTimeRemaining('')
|
||||
return
|
||||
}
|
||||
|
||||
// Display remaining time in minutes and seconds
|
||||
const minutes = Math.floor(diff / 60000)
|
||||
const seconds = Math.floor((diff % 60000) / 1000)
|
||||
setTimeRemaining(`${minutes}m ${seconds}s`)
|
||||
}
|
||||
|
||||
updateTimer()
|
||||
interval = setInterval(updateTimer, 1000)
|
||||
}
|
||||
|
||||
updateTimer()
|
||||
const interval = setInterval(updateTimer, 1000)
|
||||
return () => clearInterval(interval)
|
||||
} catch {
|
||||
setShowGodCredentials(false)
|
||||
setShowSuperGodCredentials(false)
|
||||
setTimeRemaining('')
|
||||
}
|
||||
}
|
||||
|
||||
checkCredentials()
|
||||
|
||||
void checkCredentials()
|
||||
|
||||
return () => {
|
||||
if (interval) clearInterval(interval)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleCopyPassword = async () => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Textarea } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { User, ChatCircle } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
import { Database, hashPassword } from '@/lib/database'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -12,15 +12,15 @@ import {
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table'
|
||||
} from '@/components/ui'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
} from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { MagnifyingGlass, Plus, PencilSimple, Trash, Users, ChatCircle } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
import { Database } from '@/lib/database'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
} from '@/components/ui'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog'
|
||||
} from '@/components/ui'
|
||||
import { Crown, Buildings, Users, ArrowsLeftRight, Eye, Camera } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
import { Level5Header } from './level5/Level5Header'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Lock } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
} from '@/components/ui'
|
||||
import { Plus, Trash, Play, CheckCircle, XCircle, FileCode, ArrowsOut, BookOpen, ShieldCheck } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
import { createLuaEngine, type LuaExecutionResult } from '@/lib/lua-engine'
|
||||
@@ -19,7 +19,7 @@ import type { LuaScript } from '@/lib/level-types'
|
||||
import Editor from '@monaco-editor/react'
|
||||
import { useMonaco } from '@monaco-editor/react'
|
||||
import { LuaSnippetLibrary } from '@/components/LuaSnippetLibrary'
|
||||
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
|
||||
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui'
|
||||
import { securityScanner, type SecurityScanResult } from '@/lib/security-scanner'
|
||||
import { SecurityWarningDialog } from '@/components/SecurityWarningDialog'
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useState } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
} from '@/components/ui'
|
||||
import {
|
||||
MagnifyingGlass,
|
||||
Copy,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState, useMemo } from 'react'
|
||||
import { useKV } from '@github/spark/hooks'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import type { ModelSchema, SchemaConfig } from '@/lib/schema-types'
|
||||
import { getRecordsKey, getFieldLabel, sortRecords, filterRecords, findModel } from '@/lib/schema-utils'
|
||||
import { RecordForm } from './RecordForm'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -12,16 +12,16 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
} from '@/components/ui'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
} from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import Editor from '@monaco-editor/react'
|
||||
import {
|
||||
File,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Card } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
|
||||
export type NotificationSeverity = 'info' | 'warning' | 'critical'
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Textarea } from '@/components/ui'
|
||||
import { Checkbox } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import { toast } from 'sonner'
|
||||
import { Database } from '@/lib/database'
|
||||
import { exportPackageAsZip, importPackageFromZip, downloadZip, exportDatabaseSnapshot } from '@/lib/package-export'
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import { toast } from 'sonner'
|
||||
import { Database } from '@/lib/database'
|
||||
import { PACKAGE_CATALOG } from '@/lib/package-catalog'
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui'
|
||||
import { Plus, Pencil, Trash, Eye, LockKey } from '@phosphor-icons/react'
|
||||
import { Database } from '@/lib/database'
|
||||
import { toast } from 'sonner'
|
||||
import type { PageConfig, UserRole, AppLevel } from '@/lib/level-types'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Switch } from '@/components/ui'
|
||||
|
||||
export function PageRoutesManager() {
|
||||
const [pages, setPages] = useState<PageConfig[]>([])
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState } from 'react'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
import { Warning, Eye, EyeSlash } from '@phosphor-icons/react'
|
||||
|
||||
interface PasswordChangeDialogProps {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import type { ComponentInstance } from '@/lib/builder-types'
|
||||
import { componentCatalog } from '@/lib/component-catalog'
|
||||
import { Code, PaintBrush, Trash, Palette } from '@phosphor-icons/react'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'
|
||||
import { Card } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui'
|
||||
import { Palette, ListDashes, Code, Sparkle, Package, Terminal } from '@phosphor-icons/react'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
|
||||
export function QuickGuide() {
|
||||
return (
|
||||
|
||||
@@ -236,7 +236,7 @@ import { ComponentCatalog } from '@/components/ComponentCatalog'
|
||||
|
||||
### New Way (recommended)
|
||||
```typescript
|
||||
import { Button } from '@/components/atoms'
|
||||
import { Button } from '@/components/ui'
|
||||
import { ComponentCatalog } from '@/components/organisms'
|
||||
```
|
||||
|
||||
@@ -271,7 +271,7 @@ export function FormField({ label, error, ...props }: FormFieldProps) {
|
||||
// Organism handling data fetching and display
|
||||
import { useKV } from '@github/spark/hooks'
|
||||
import { Button } from '@/components/atoms'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Card } from '@/components/ui'
|
||||
|
||||
export function UserList() {
|
||||
const [users] = useKV('users', [])
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import type { ModelSchema, SchemaConfig } from '@/lib/schema-types'
|
||||
import { validateRecord, createEmptyRecord } from '@/lib/schema-utils'
|
||||
import { FieldRenderer } from './FieldRenderer'
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import type { ComponentInstance } from '@/lib/builder-types'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Alert } from '@/components/ui/alert'
|
||||
import { Progress } from '@/components/ui/progress'
|
||||
import { Slider } from '@/components/ui/slider'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Textarea } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Card } from '@/components/ui'
|
||||
import { Switch } from '@/components/ui'
|
||||
import { Checkbox } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import { Alert } from '@/components/ui'
|
||||
import { Progress } from '@/components/ui'
|
||||
import { Slider } from '@/components/ui'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui'
|
||||
import { IRCWebchatDeclarative } from '@/components/IRCWebchatDeclarative'
|
||||
import { NotificationSummaryCard } from '@/components/NotificationSummaryCard'
|
||||
import type { User } from '@/lib/level-types'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Switch } from '@/components/ui'
|
||||
import { Envelope, FloppyDisk, PaperPlaneTilt } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
import { Database } from '@/lib/database'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
import type { SchemaConfig } from '@/lib/schema-types'
|
||||
import { FloppyDisk, X, Warning } from '@phosphor-icons/react'
|
||||
import Editor from '@monaco-editor/react'
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
} from '@/components/ui'
|
||||
import { Switch } from '@/components/ui'
|
||||
import { Plus, Trash } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
import type { ModelSchema, FieldSchema, FieldType } from '@/lib/schema-types'
|
||||
|
||||
@@ -6,12 +6,12 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
} from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import { ShieldWarning, Warning, Info, CheckCircle } from '@phosphor-icons/react'
|
||||
import type { SecurityScanResult, SecurityIssue } from '@/lib/security-scanner'
|
||||
import { getSeverityColor, getSeverityIcon } from '@/lib/security-scanner'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { Switch } from '@/components/ui'
|
||||
import { Palette, Sun, Moon, FloppyDisk, ArrowCounterClockwise } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
import { useKV } from '@github/spark/hooks'
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { SignIn, UserPlus, ArrowLeft, Envelope } from '@phosphor-icons/react'
|
||||
import { toast } from 'sonner'
|
||||
import { Database, hashPassword } from '@/lib/database'
|
||||
import { generateScrambledPassword, simulateEmailSend } from '@/lib/password-utils'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
|
||||
interface UnifiedLoginProps {
|
||||
onLogin: (credentials: { username: string; password: string }) => void
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Textarea } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui'
|
||||
import { Plus, Pencil, Trash, UserCircle } from '@phosphor-icons/react'
|
||||
import { Database, hashPassword } from '@/lib/database'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// This file serves as a reference for building well-documented components
|
||||
|
||||
import { useState, useCallback } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { useAuth } from '@/hooks'
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
|
||||
export function ContactSection() {
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
|
||||
export function FeaturesSection() {
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Warning, Eye, EyeSlash, Copy, Check } from '@phosphor-icons/react'
|
||||
|
||||
interface GodCredentialsBannerProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Button } from '@/components/ui'
|
||||
|
||||
interface HeroSectionProps {
|
||||
onNavigate: (level: number) => void
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Button } from '@/components/ui'
|
||||
import { List, X, User, ShieldCheck } from '@phosphor-icons/react'
|
||||
|
||||
interface NavigationBarProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui'
|
||||
import { Trash } from '@phosphor-icons/react'
|
||||
import type { Comment, User } from '@/lib/level-types'
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Textarea } from '@/components/ui'
|
||||
import { Envelope } from '@phosphor-icons/react'
|
||||
import type { User } from '@/lib/level-types'
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
} from '@/components/ui'
|
||||
import { SignOut, Eye, House, Download, Upload, Terminal } from '@phosphor-icons/react'
|
||||
|
||||
interface Level4HeaderProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import { Database as DatabaseIcon, Lightning, Code, BookOpen, HardDrives, MapTrifold, Tree, Users, Gear, Palette, ListDashes, Sparkle, Package } from '@phosphor-icons/react'
|
||||
import { SchemaEditorLevel4 } from '@/components/SchemaEditorLevel4'
|
||||
import { WorkflowEditor } from '@/components/WorkflowEditor'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Shield, Users } from '@phosphor-icons/react'
|
||||
import type { User } from '@/lib/level-types'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Crown, SignOut, Terminal } from '@phosphor-icons/react'
|
||||
|
||||
interface Level5HeaderProps {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
import { Crown, ArrowsLeftRight } from '@phosphor-icons/react'
|
||||
import type { User } from '@/lib/level-types'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { Eye } from '@phosphor-icons/react'
|
||||
|
||||
interface PreviewTabProps {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Buildings, House } from '@phosphor-icons/react'
|
||||
import type { Tenant, User } from '@/lib/level-types'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { SignOut, House } from '@phosphor-icons/react'
|
||||
|
||||
interface AppHeaderProps {
|
||||
|
||||
@@ -29,10 +29,10 @@ ui/
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
// Import from the main index
|
||||
// Prefer the main barrel import
|
||||
import { Button, Card, Table, Dialog } from '@/components/ui'
|
||||
|
||||
// Or import from specific category
|
||||
// Category imports are available when you want stricter layering
|
||||
import { Button, Input } from '@/components/ui/atoms'
|
||||
import { Card, Select } from '@/components/ui/molecules'
|
||||
import { Table, Form } from '@/components/ui/organisms'
|
||||
@@ -79,8 +79,8 @@ import styles from './MyComponent.module.scss'
|
||||
|
||||
## Legacy Files
|
||||
|
||||
⚠️ **Files in the root of this directory (like `button.tsx`, `dialog.tsx`, etc.) are LEGACY**
|
||||
and will be removed. Only use components from:
|
||||
⚠️ **Root wrapper files (like `button.tsx`, `dialog.tsx`, etc.) are legacy** and exist for
|
||||
backward compatibility. Avoid `@/components/ui/<component>` imports and use:
|
||||
- `atoms/`
|
||||
- `molecules/`
|
||||
- `organisms/`
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './molecules/Accordion'
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export {
|
||||
AlertDialog,
|
||||
AlertDialogTrigger,
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Alert, AlertTitle, AlertDescription, type AlertVariant, type AlertProps } from './molecules/Alert'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Avatar, AvatarImage, AvatarFallback, type AvatarProps } from './atoms/Avatar'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Badge, type BadgeProps, type BadgeVariant } from './atoms/Badge'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Button, type ButtonProps, type ButtonVariant, type ButtonSize } from './atoms/Button'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './molecules/Card'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Checkbox, type CheckboxProps } from './atoms/Checkbox'
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
|
||||
13
frontends/nextjs/src/components/ui/index.test.ts
Normal file
13
frontends/nextjs/src/components/ui/index.test.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { Button, Card, Dialog, Select, Table, Tabs } from '@/components/ui'
|
||||
|
||||
describe('ui barrel exports', () => {
|
||||
it('exposes common UI components', () => {
|
||||
expect(Button).toBeDefined()
|
||||
expect(Card).toBeDefined()
|
||||
expect(Dialog).toBeDefined()
|
||||
expect(Select).toBeDefined()
|
||||
expect(Table).toBeDefined()
|
||||
expect(Tabs).toBeDefined()
|
||||
})
|
||||
})
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Input, type InputProps } from './atoms/Input'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Label, type LabelProps } from './atoms/Label'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Progress, type ProgressProps } from './atoms/Progress'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { RadioGroup, RadioGroupItem } from './molecules/RadioGroup'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { ScrollArea, ScrollBar, type ScrollAreaProps } from './atoms/ScrollArea'
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Separator, type SeparatorProps } from './atoms/Separator'
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export {
|
||||
Sheet,
|
||||
SheetTrigger,
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Skeleton, type SkeletonProps } from './atoms/Skeleton'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Slider, type SliderProps } from './atoms/Slider'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Switch, type SwitchProps } from './atoms/Switch'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption } from './organisms/Table'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent } from './molecules/Tabs'
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
// Re-export for backward compatibility
|
||||
// TODO: Update imports to use @/components/ui directly
|
||||
export { Textarea, type TextareaProps } from './atoms/Textarea'
|
||||
|
||||
@@ -2,26 +2,64 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
||||
import { checkRateLimit } from './check-rate-limit'
|
||||
import { clearRateLimit } from './clear-rate-limit'
|
||||
import { clearAllRateLimits } from './clear-all-rate-limits'
|
||||
import { MAX_REQUESTS_PER_WINDOW, RATE_LIMIT_WINDOW } from './rate-limit-store'
|
||||
import { DEFAULT_MAX_REQUESTS_PER_WINDOW, DEFAULT_RATE_LIMIT_WINDOW_MS, getRateLimitConfig } from './rate-limit-store'
|
||||
|
||||
const BASE_TIME = new Date('2024-01-01T00:00:00Z')
|
||||
const ENV_RATE_LIMIT_WINDOW_MS = 'MB_RATE_LIMIT_WINDOW_MS'
|
||||
const ENV_MAX_REQUESTS = 'MB_RATE_LIMIT_MAX_REQUESTS'
|
||||
|
||||
const resetRateLimitEnv = () => {
|
||||
delete process.env[ENV_RATE_LIMIT_WINDOW_MS]
|
||||
delete process.env[ENV_MAX_REQUESTS]
|
||||
}
|
||||
|
||||
describe('rate limit helpers', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(BASE_TIME)
|
||||
clearAllRateLimits()
|
||||
resetRateLimitEnv()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
clearAllRateLimits()
|
||||
vi.useRealTimers()
|
||||
resetRateLimitEnv()
|
||||
})
|
||||
|
||||
describe('rate limit config', () => {
|
||||
it('uses defaults when env is unset', () => {
|
||||
const { windowMs, maxRequests } = getRateLimitConfig()
|
||||
|
||||
expect(windowMs).toBe(DEFAULT_RATE_LIMIT_WINDOW_MS)
|
||||
expect(maxRequests).toBe(DEFAULT_MAX_REQUESTS_PER_WINDOW)
|
||||
})
|
||||
|
||||
it('uses env overrides when valid', () => {
|
||||
process.env[ENV_RATE_LIMIT_WINDOW_MS] = '120000'
|
||||
process.env[ENV_MAX_REQUESTS] = '250'
|
||||
|
||||
const { windowMs, maxRequests } = getRateLimitConfig()
|
||||
|
||||
expect(windowMs).toBe(120000)
|
||||
expect(maxRequests).toBe(250)
|
||||
})
|
||||
|
||||
it('falls back to defaults for invalid env values', () => {
|
||||
process.env[ENV_RATE_LIMIT_WINDOW_MS] = '-10'
|
||||
process.env[ENV_MAX_REQUESTS] = 'not-a-number'
|
||||
|
||||
const { windowMs, maxRequests } = getRateLimitConfig()
|
||||
|
||||
expect(windowMs).toBe(DEFAULT_RATE_LIMIT_WINDOW_MS)
|
||||
expect(maxRequests).toBe(DEFAULT_MAX_REQUESTS_PER_WINDOW)
|
||||
})
|
||||
})
|
||||
|
||||
describe('checkRateLimit', () => {
|
||||
it.each([
|
||||
{ name: 'allows requests up to the limit', attempts: MAX_REQUESTS_PER_WINDOW, expected: true },
|
||||
{ name: 'blocks requests over the limit', attempts: MAX_REQUESTS_PER_WINDOW + 1, expected: false },
|
||||
{ name: 'allows requests up to the limit', attempts: DEFAULT_MAX_REQUESTS_PER_WINDOW, expected: true },
|
||||
{ name: 'blocks requests over the limit', attempts: DEFAULT_MAX_REQUESTS_PER_WINDOW + 1, expected: false },
|
||||
])('$name', ({ attempts, expected }) => {
|
||||
const userId = 'user-limit'
|
||||
let lastResult = true
|
||||
@@ -34,12 +72,12 @@ describe('rate limit helpers', () => {
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ name: 'block before the window expires', advanceMs: RATE_LIMIT_WINDOW - 1, expected: false },
|
||||
{ name: 'allow after the window expires', advanceMs: RATE_LIMIT_WINDOW + 1, expected: true },
|
||||
{ name: 'block before the window expires', advanceMs: DEFAULT_RATE_LIMIT_WINDOW_MS - 1, expected: false },
|
||||
{ name: 'allow after the window expires', advanceMs: DEFAULT_RATE_LIMIT_WINDOW_MS + 1, expected: true },
|
||||
])('should $name', ({ advanceMs, expected }) => {
|
||||
const userId = 'user-window'
|
||||
|
||||
for (let i = 0; i < MAX_REQUESTS_PER_WINDOW; i += 1) {
|
||||
for (let i = 0; i < DEFAULT_MAX_REQUESTS_PER_WINDOW; i += 1) {
|
||||
checkRateLimit(userId)
|
||||
}
|
||||
|
||||
@@ -49,11 +87,21 @@ describe('rate limit helpers', () => {
|
||||
|
||||
expect(checkRateLimit(userId)).toBe(expected)
|
||||
})
|
||||
|
||||
it('respects env-configured limits', () => {
|
||||
process.env[ENV_RATE_LIMIT_WINDOW_MS] = '1000'
|
||||
process.env[ENV_MAX_REQUESTS] = '2'
|
||||
const userId = 'user-env'
|
||||
|
||||
expect(checkRateLimit(userId)).toBe(true)
|
||||
expect(checkRateLimit(userId)).toBe(true)
|
||||
expect(checkRateLimit(userId)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('clearRateLimit', () => {
|
||||
it.each([{ userId: 'user-a' }, { userId: 'user-b' }])('should reset for $userId', ({ userId }) => {
|
||||
for (let i = 0; i < MAX_REQUESTS_PER_WINDOW; i += 1) {
|
||||
for (let i = 0; i < DEFAULT_MAX_REQUESTS_PER_WINDOW; i += 1) {
|
||||
checkRateLimit(userId)
|
||||
}
|
||||
|
||||
@@ -71,7 +119,7 @@ describe('rate limit helpers', () => {
|
||||
{ users: ['user-x', 'user-y', 'user-z'] },
|
||||
])('should reset all users: $users', ({ users }) => {
|
||||
for (const userId of users) {
|
||||
for (let i = 0; i < MAX_REQUESTS_PER_WINDOW; i += 1) {
|
||||
for (let i = 0; i < DEFAULT_MAX_REQUESTS_PER_WINDOW; i += 1) {
|
||||
checkRateLimit(userId)
|
||||
}
|
||||
expect(checkRateLimit(userId)).toBe(false)
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { RATE_LIMIT_WINDOW, MAX_REQUESTS_PER_WINDOW, rateLimitMap } from './rate-limit-store'
|
||||
import { getRateLimitConfig, rateLimitMap } from './rate-limit-store'
|
||||
|
||||
/**
|
||||
* Check if user is within rate limits
|
||||
*/
|
||||
export function checkRateLimit(userId: string): boolean {
|
||||
const { windowMs, maxRequests } = getRateLimitConfig()
|
||||
const now = Date.now()
|
||||
const userRequests = rateLimitMap.get(userId) || []
|
||||
|
||||
const recentRequests = userRequests.filter(
|
||||
timestamp => now - timestamp < RATE_LIMIT_WINDOW
|
||||
timestamp => now - timestamp < windowMs
|
||||
)
|
||||
|
||||
if (recentRequests.length >= MAX_REQUESTS_PER_WINDOW) {
|
||||
if (recentRequests.length >= maxRequests) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
// TODO: Load rate limit settings from config/DB instead of hardcoding.
|
||||
const RATE_LIMIT_WINDOW = 60000
|
||||
const MAX_REQUESTS_PER_WINDOW = 100
|
||||
const DEFAULT_RATE_LIMIT_WINDOW_MS = 60000
|
||||
const DEFAULT_MAX_REQUESTS_PER_WINDOW = 100
|
||||
|
||||
const parsePositiveInt = (value: string | undefined, fallback: number): number => {
|
||||
if (!value) return fallback
|
||||
|
||||
const parsed = Number.parseInt(value, 10)
|
||||
if (!Number.isFinite(parsed) || parsed <= 0) return fallback
|
||||
|
||||
return parsed
|
||||
}
|
||||
|
||||
export const getRateLimitConfig = () => ({
|
||||
windowMs: parsePositiveInt(process.env.MB_RATE_LIMIT_WINDOW_MS, DEFAULT_RATE_LIMIT_WINDOW_MS),
|
||||
maxRequests: parsePositiveInt(process.env.MB_RATE_LIMIT_MAX_REQUESTS, DEFAULT_MAX_REQUESTS_PER_WINDOW),
|
||||
})
|
||||
|
||||
const rateLimitMap = new Map<string, number[]>()
|
||||
|
||||
export { RATE_LIMIT_WINDOW, MAX_REQUESTS_PER_WINDOW, rateLimitMap }
|
||||
export { DEFAULT_RATE_LIMIT_WINDOW_MS, DEFAULT_MAX_REQUESTS_PER_WINDOW, rateLimitMap }
|
||||
|
||||
@@ -1,8 +1,294 @@
|
||||
#!/usr/bin/env tsx
|
||||
|
||||
console.log(JSON.stringify({
|
||||
averageRenderTime: 12,
|
||||
slowComponents: [],
|
||||
recommendations: [],
|
||||
timestamp: new Date().toISOString()
|
||||
}, null, 2))
|
||||
import { existsSync, readdirSync, readFileSync, statSync } from 'fs'
|
||||
import { basename, extname, join, relative } from 'path'
|
||||
|
||||
interface HookCounts {
|
||||
[key: string]: number
|
||||
}
|
||||
|
||||
interface ComponentMetrics {
|
||||
file: string
|
||||
component: string
|
||||
lines: number
|
||||
bytes: number
|
||||
hooks: {
|
||||
builtIn: number
|
||||
custom: number
|
||||
total: number
|
||||
byHook: HookCounts
|
||||
}
|
||||
effects: number
|
||||
memoization: number
|
||||
estimatedRenderTimeMs: number
|
||||
reasons: string[]
|
||||
risk: 'low' | 'medium' | 'high'
|
||||
}
|
||||
|
||||
const BUILTIN_HOOKS = [
|
||||
'useState',
|
||||
'useReducer',
|
||||
'useEffect',
|
||||
'useLayoutEffect',
|
||||
'useInsertionEffect',
|
||||
'useMemo',
|
||||
'useCallback',
|
||||
'useRef',
|
||||
'useContext',
|
||||
'useSyncExternalStore',
|
||||
'useTransition',
|
||||
'useDeferredValue',
|
||||
'useId',
|
||||
'useImperativeHandle',
|
||||
]
|
||||
|
||||
const BUILTIN_HOOK_SET = new Set(BUILTIN_HOOKS)
|
||||
const SKIP_DIRS = new Set([
|
||||
'node_modules',
|
||||
'.next',
|
||||
'dist',
|
||||
'build',
|
||||
'coverage',
|
||||
'.git',
|
||||
'__tests__',
|
||||
'__mocks__',
|
||||
'__snapshots__',
|
||||
])
|
||||
|
||||
const THRESHOLDS = {
|
||||
slowRenderMs: 16,
|
||||
largeComponentLines: 200,
|
||||
veryLargeComponentLines: 300,
|
||||
highHookCount: 12,
|
||||
highEffectCount: 3,
|
||||
}
|
||||
|
||||
const TARGET_EXTENSIONS = new Set(['.tsx'])
|
||||
|
||||
function countMatches(content: string, regex: RegExp): number {
|
||||
return content.match(regex)?.length ?? 0
|
||||
}
|
||||
|
||||
function pickSourceRoot(): string | null {
|
||||
const candidates = [
|
||||
process.env.RENDER_ANALYSIS_ROOT,
|
||||
join(process.cwd(), 'frontends', 'nextjs', 'src'),
|
||||
join(process.cwd(), 'src'),
|
||||
].filter(Boolean) as string[]
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (existsSync(candidate)) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function walkDir(dir: string, files: string[]): void {
|
||||
let entries: string[]
|
||||
try {
|
||||
entries = readdirSync(dir)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(dir, entry)
|
||||
let stats
|
||||
try {
|
||||
stats = statSync(fullPath)
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
if (SKIP_DIRS.has(entry)) {
|
||||
continue
|
||||
}
|
||||
walkDir(fullPath, files)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!stats.isFile()) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (!TARGET_EXTENSIONS.has(extname(entry))) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (entry.endsWith('.test.tsx') || entry.endsWith('.spec.tsx') || entry.endsWith('.stories.tsx')) {
|
||||
continue
|
||||
}
|
||||
|
||||
files.push(fullPath)
|
||||
}
|
||||
}
|
||||
|
||||
function estimateRenderTimeMs(lines: number, hooks: number, effects: number, memoization: number): number {
|
||||
const base = 1.5
|
||||
const lineCost = Math.min(lines, 400) * 0.03
|
||||
const hookCost = hooks * 0.4
|
||||
const effectCost = effects * 0.8
|
||||
const memoSavings = Math.min(memoization, 4) * 0.3
|
||||
const estimate = base + lineCost + hookCost + effectCost - memoSavings
|
||||
return Math.max(0.5, Math.round(estimate * 10) / 10)
|
||||
}
|
||||
|
||||
function analyzeFile(filePath: string): ComponentMetrics | null {
|
||||
let content = ''
|
||||
try {
|
||||
content = readFileSync(filePath, 'utf8')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
|
||||
const lines = content.split(/\r?\n/).length
|
||||
const bytes = Buffer.byteLength(content, 'utf8')
|
||||
|
||||
const byHook: HookCounts = {}
|
||||
let builtInCount = 0
|
||||
|
||||
for (const hook of BUILTIN_HOOKS) {
|
||||
const count = countMatches(content, new RegExp(`\\b${hook}\\b`, 'g'))
|
||||
byHook[hook] = count
|
||||
builtInCount += count
|
||||
}
|
||||
|
||||
const allHookCalls = content.match(/\buse[A-Z]\w*\b/g) ?? []
|
||||
const customHookCount = Math.max(0, allHookCalls.filter(hook => !BUILTIN_HOOK_SET.has(hook)).length)
|
||||
const hookCount = builtInCount + customHookCount
|
||||
|
||||
const effectCount = (byHook.useEffect ?? 0) + (byHook.useLayoutEffect ?? 0) + (byHook.useInsertionEffect ?? 0)
|
||||
const memoCount = (byHook.useMemo ?? 0) + (byHook.useCallback ?? 0)
|
||||
const reactMemoCount = countMatches(content, /\bReact\.memo\b/g)
|
||||
const memoCallCount = countMatches(content, /\bmemo\s*\(/g)
|
||||
const memoization = memoCount + reactMemoCount + Math.max(0, memoCallCount - reactMemoCount)
|
||||
|
||||
const estimatedRenderTimeMs = estimateRenderTimeMs(lines, hookCount, effectCount, memoization)
|
||||
const reasons: string[] = []
|
||||
|
||||
if (lines >= THRESHOLDS.veryLargeComponentLines) {
|
||||
reasons.push(`Very large component: ${lines} lines`)
|
||||
} else if (lines >= THRESHOLDS.largeComponentLines) {
|
||||
reasons.push(`Large component: ${lines} lines`)
|
||||
}
|
||||
|
||||
if (hookCount >= THRESHOLDS.highHookCount) {
|
||||
reasons.push(`High hook count: ${hookCount}`)
|
||||
}
|
||||
|
||||
if (effectCount >= THRESHOLDS.highEffectCount) {
|
||||
reasons.push(`Multiple effects: ${effectCount}`)
|
||||
}
|
||||
|
||||
if (estimatedRenderTimeMs >= THRESHOLDS.slowRenderMs) {
|
||||
reasons.push(`Estimated render time: ${estimatedRenderTimeMs}ms`)
|
||||
}
|
||||
|
||||
let risk: ComponentMetrics['risk'] = 'low'
|
||||
if (reasons.length >= 3 || estimatedRenderTimeMs >= THRESHOLDS.slowRenderMs) {
|
||||
risk = 'high'
|
||||
} else if (reasons.length >= 1) {
|
||||
risk = 'medium'
|
||||
}
|
||||
|
||||
return {
|
||||
file: relative(process.cwd(), filePath),
|
||||
component: basename(filePath, '.tsx'),
|
||||
lines,
|
||||
bytes,
|
||||
hooks: {
|
||||
builtIn: builtInCount,
|
||||
custom: customHookCount,
|
||||
total: hookCount,
|
||||
byHook,
|
||||
},
|
||||
effects: effectCount,
|
||||
memoization,
|
||||
estimatedRenderTimeMs,
|
||||
reasons,
|
||||
risk,
|
||||
}
|
||||
}
|
||||
|
||||
function buildRecommendations(slowComponents: ComponentMetrics[]): string[] {
|
||||
const recommendations: string[] = []
|
||||
|
||||
if (slowComponents.length === 0) {
|
||||
recommendations.push('No high-risk components detected. Re-run after significant UI changes.')
|
||||
return recommendations
|
||||
}
|
||||
|
||||
if (slowComponents.some(component => component.lines >= THRESHOLDS.veryLargeComponentLines)) {
|
||||
recommendations.push('Split components over 300 lines into smaller pieces to reduce render work.')
|
||||
}
|
||||
|
||||
if (slowComponents.some(component => component.effects >= THRESHOLDS.highEffectCount)) {
|
||||
recommendations.push('Reduce the number of effects per component by extracting side effects into hooks.')
|
||||
}
|
||||
|
||||
if (slowComponents.some(component => component.hooks.total >= THRESHOLDS.highHookCount)) {
|
||||
recommendations.push('Consider splitting stateful logic across smaller components or hooks.')
|
||||
}
|
||||
|
||||
if (slowComponents.some(component => component.memoization === 0 && component.estimatedRenderTimeMs >= THRESHOLDS.slowRenderMs)) {
|
||||
recommendations.push('Add memoization (React.memo/useMemo/useCallback) where render work is heavy.')
|
||||
}
|
||||
|
||||
if (recommendations.length === 0) {
|
||||
recommendations.push('Review flagged components for unnecessary renders or expensive computations.')
|
||||
}
|
||||
|
||||
return recommendations
|
||||
}
|
||||
|
||||
const rootDir = pickSourceRoot()
|
||||
|
||||
if (!rootDir) {
|
||||
console.log(JSON.stringify({
|
||||
analysisType: 'static-heuristic',
|
||||
averageRenderTime: 0,
|
||||
slowComponents: [],
|
||||
recommendations: ['No source directory found to analyze.'],
|
||||
timestamp: new Date().toISOString(),
|
||||
}, null, 2))
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const files: string[] = []
|
||||
walkDir(rootDir, files)
|
||||
|
||||
const metrics: ComponentMetrics[] = files
|
||||
.map(file => analyzeFile(file))
|
||||
.filter((result): result is ComponentMetrics => result !== null)
|
||||
|
||||
const averageRenderTime = metrics.length === 0
|
||||
? 0
|
||||
: Math.round((metrics.reduce((sum, metric) => sum + metric.estimatedRenderTimeMs, 0) / metrics.length) * 10) / 10
|
||||
|
||||
const slowComponents = metrics
|
||||
.filter(metric => metric.reasons.length > 0 || metric.estimatedRenderTimeMs >= THRESHOLDS.slowRenderMs)
|
||||
.sort((a, b) => b.estimatedRenderTimeMs - a.estimatedRenderTimeMs)
|
||||
|
||||
const topByLines = [...metrics].sort((a, b) => b.lines - a.lines).slice(0, 10)
|
||||
const topByHooks = [...metrics].sort((a, b) => b.hooks.total - a.hooks.total).slice(0, 10)
|
||||
|
||||
const summary = {
|
||||
analysisType: 'static-heuristic',
|
||||
rootDir: relative(process.cwd(), rootDir) || '.',
|
||||
componentsAnalyzed: metrics.length,
|
||||
averageRenderTime,
|
||||
averageRenderTimeMs: averageRenderTime,
|
||||
slowComponentsTotal: slowComponents.length,
|
||||
thresholds: THRESHOLDS,
|
||||
slowComponents: slowComponents.slice(0, 15),
|
||||
topByLines,
|
||||
topByHooks,
|
||||
recommendations: buildRecommendations(slowComponents),
|
||||
note: 'Estimated render times are derived from file size and hook usage. Use React Profiler for real timings.',
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(summary, null, 2))
|
||||
|
||||
Reference in New Issue
Block a user