@orsetra/shared-ui 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/components/layout/index.ts +31 -0
  2. package/components/layout/layout-container.tsx +85 -0
  3. package/components/layout/root-layout-wrapper.tsx +33 -0
  4. package/components/layout/sidebar/data.tsx +88 -0
  5. package/components/layout/sidebar/main-sidebar.tsx +177 -0
  6. package/components/layout/sidebar/sidebar.tsx +750 -0
  7. package/components/layout/skeleton.tsx +15 -0
  8. package/components/ui/accordion.tsx +58 -0
  9. package/components/ui/alert-dialog.tsx +133 -0
  10. package/components/ui/alert.tsx +59 -0
  11. package/components/ui/aspect-ratio.tsx +7 -0
  12. package/components/ui/assets-header.tsx +50 -0
  13. package/components/ui/avatar.tsx +50 -0
  14. package/components/ui/badge.tsx +54 -0
  15. package/components/ui/breadcrumb.tsx +115 -0
  16. package/components/ui/button.tsx +83 -0
  17. package/components/ui/calendar.tsx +66 -0
  18. package/components/ui/card.tsx +79 -0
  19. package/components/ui/carousel.tsx +262 -0
  20. package/components/ui/certificate-editor.tsx +445 -0
  21. package/components/ui/chart.tsx +365 -0
  22. package/components/ui/checkbox.tsx +30 -0
  23. package/components/ui/collapsible.tsx +11 -0
  24. package/components/ui/command.tsx +153 -0
  25. package/components/ui/context-menu.tsx +200 -0
  26. package/components/ui/dialog.tsx +122 -0
  27. package/components/ui/drawer.tsx +118 -0
  28. package/components/ui/dropdown-menu.tsx +200 -0
  29. package/components/ui/environment-settings.tsx +173 -0
  30. package/components/ui/environment-variables-config.tsx +175 -0
  31. package/components/ui/file-import.tsx +177 -0
  32. package/components/ui/form.tsx +178 -0
  33. package/components/ui/hover-card.tsx +29 -0
  34. package/components/ui/index.ts +54 -0
  35. package/components/ui/input-otp.tsx +71 -0
  36. package/components/ui/input.tsx +23 -0
  37. package/components/ui/label.tsx +26 -0
  38. package/components/ui/logo.tsx +17 -0
  39. package/components/ui/menubar.tsx +236 -0
  40. package/components/ui/navigation-menu.tsx +128 -0
  41. package/components/ui/page-header.tsx +35 -0
  42. package/components/ui/pagination.tsx +112 -0
  43. package/components/ui/popover.tsx +31 -0
  44. package/components/ui/process-status.tsx +98 -0
  45. package/components/ui/progress.tsx +31 -0
  46. package/components/ui/radio-group.tsx +44 -0
  47. package/components/ui/resizable.tsx +45 -0
  48. package/components/ui/resource-settings.tsx +227 -0
  49. package/components/ui/scroll-area.tsx +48 -0
  50. package/components/ui/search-input.tsx +26 -0
  51. package/components/ui/secret-explorer.tsx +274 -0
  52. package/components/ui/secret-properties-editor.tsx +642 -0
  53. package/components/ui/select.tsx +162 -0
  54. package/components/ui/selected-asset.tsx +120 -0
  55. package/components/ui/separator.tsx +31 -0
  56. package/components/ui/sheet.tsx +140 -0
  57. package/components/ui/skeleton.tsx +15 -0
  58. package/components/ui/slider.tsx +28 -0
  59. package/components/ui/sonner.tsx +31 -0
  60. package/components/ui/switch.tsx +29 -0
  61. package/components/ui/table.tsx +117 -0
  62. package/components/ui/tabs.tsx +55 -0
  63. package/components/ui/textarea.tsx +22 -0
  64. package/components/ui/toast.tsx +131 -0
  65. package/components/ui/toaster.tsx +35 -0
  66. package/components/ui/toggle-group.tsx +61 -0
  67. package/components/ui/toggle.tsx +45 -0
  68. package/components/ui/tooltip.tsx +30 -0
  69. package/components/ui/user-menu.tsx +86 -0
  70. package/hooks/index.ts +5 -0
  71. package/hooks/use-auth.ts +10 -0
  72. package/hooks/use-mobile.ts +19 -0
  73. package/hooks/use-toast.ts +8 -0
  74. package/hooks/use-websocket.tsx +76 -0
  75. package/index.ts +11 -1
  76. package/lib/menu-utils.ts +48 -0
  77. package/package.json +36 -8
@@ -0,0 +1,112 @@
1
+ import * as React from "react"
2
+ import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
3
+
4
+ import { cn } from "../../lib/utils"
5
+ import { ButtonProps, variants } from "./button"
6
+
7
+ const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
8
+ <nav
9
+ role="navigation"
10
+ aria-label="pagination"
11
+ className={cn("mx-auto flex w-full justify-center", className)}
12
+ {...props}
13
+ />
14
+ )
15
+ Pagination.displayName = "Pagination"
16
+
17
+ const PaginationContent = React.forwardRef<
18
+ HTMLUListElement,
19
+ React.ComponentProps<"ul">
20
+ >(({ className, ...props }, ref) => (
21
+ <ul
22
+ ref={ref}
23
+ className={cn("flex flex-row items-center gap-1", className)}
24
+ {...props}
25
+ />
26
+ ))
27
+ PaginationContent.displayName = "PaginationContent"
28
+
29
+ const PaginationItem = React.forwardRef<
30
+ HTMLLIElement,
31
+ React.ComponentProps<"li">
32
+ >(({ className, ...props }, ref) => (
33
+ <li ref={ref} className={cn("", className)} {...props} />
34
+ ))
35
+ PaginationItem.displayName = "PaginationItem"
36
+
37
+ type PaginationLinkProps = {
38
+ isActive?: boolean
39
+ } & Pick<ButtonProps, "size"> &
40
+ React.ComponentProps<"a">
41
+
42
+ const PaginationLink = ({
43
+ className,
44
+ isActive,
45
+ size = "sm",
46
+ ...props
47
+ }: PaginationLinkProps) => (
48
+ <a
49
+ aria-current={isActive ? "page" : undefined}
50
+ className={cn(
51
+ variants[isActive ? "secondary" : "ghost"],
52
+ className
53
+ )}
54
+ {...props}
55
+ />
56
+ )
57
+ PaginationLink.displayName = "PaginationLink"
58
+
59
+ const PaginationPrevious = ({
60
+ className,
61
+ ...props
62
+ }: React.ComponentProps<typeof PaginationLink>) => (
63
+ <PaginationLink
64
+ aria-label="Go to previous page"
65
+ className={cn("gap-1 pl-2.5", className)}
66
+ {...props}
67
+ >
68
+ <ChevronLeft className="h-4 w-4" />
69
+ <span>Previous</span>
70
+ </PaginationLink>
71
+ )
72
+ PaginationPrevious.displayName = "PaginationPrevious"
73
+
74
+ const PaginationNext = ({
75
+ className,
76
+ ...props
77
+ }: React.ComponentProps<typeof PaginationLink>) => (
78
+ <PaginationLink
79
+ aria-label="Go to next page"
80
+ className={cn("gap-1 pr-2.5", className)}
81
+ {...props}
82
+ >
83
+ <span>Next</span>
84
+ <ChevronRight className="h-4 w-4" />
85
+ </PaginationLink>
86
+ )
87
+ PaginationNext.displayName = "PaginationNext"
88
+
89
+ const PaginationEllipsis = ({
90
+ className,
91
+ ...props
92
+ }: React.ComponentProps<"span">) => (
93
+ <span
94
+ aria-hidden
95
+ className={cn("flex h-9 w-9 items-center justify-center", className)}
96
+ {...props}
97
+ >
98
+ <MoreHorizontal className="h-4 w-4" />
99
+ <span className="sr-only">More pages</span>
100
+ </span>
101
+ )
102
+ PaginationEllipsis.displayName = "PaginationEllipsis"
103
+
104
+ export {
105
+ Pagination,
106
+ PaginationContent,
107
+ PaginationEllipsis,
108
+ PaginationItem,
109
+ PaginationLink,
110
+ PaginationNext,
111
+ PaginationPrevious,
112
+ }
@@ -0,0 +1,31 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as PopoverPrimitive from "@radix-ui/react-popover"
5
+
6
+ import { cn } from "../../lib/utils"
7
+
8
+ const Popover = PopoverPrimitive.Root
9
+
10
+ const PopoverTrigger = PopoverPrimitive.Trigger
11
+
12
+ const PopoverContent = React.forwardRef<
13
+ React.ElementRef<typeof PopoverPrimitive.Content>,
14
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
15
+ >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
16
+ <PopoverPrimitive.Portal>
17
+ <PopoverPrimitive.Content
18
+ ref={ref}
19
+ align={align}
20
+ sideOffset={sideOffset}
21
+ className={cn(
22
+ "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ </PopoverPrimitive.Portal>
28
+ ))
29
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName
30
+
31
+ export { Popover, PopoverTrigger, PopoverContent }
@@ -0,0 +1,98 @@
1
+ "use client"
2
+
3
+ import { ReactNode, useEffect, useState } from 'react'
4
+ import { getExecutionStatus } from '@/app/vpcs/workflow-service'
5
+ import { useToast } from '../../hooks/use-toast'
6
+ import { X } from 'lucide-react'
7
+ import { Button } from './button'
8
+
9
+ interface ProcessStatusProps {
10
+ title: string;
11
+ description: string;
12
+ executionArn: string;
13
+ onSuccess?: () => void;
14
+ onError?: (error: string) => void;
15
+ onClose?: () => void;
16
+ icon?: ReactNode;
17
+ }
18
+
19
+ export function ProcessStatus({
20
+ title,
21
+ description,
22
+ executionArn,
23
+ onSuccess,
24
+ onError,
25
+ onClose,
26
+ icon = "⚡"
27
+ }: ProcessStatusProps) {
28
+ const { toast } = useToast()
29
+ const [status, setStatus] = useState<'running' | 'success' | 'error'>('running')
30
+
31
+ useEffect(() => {
32
+ const checkStatus = async () => {
33
+ try {
34
+ const execution = await getExecutionStatus(executionArn)
35
+
36
+ if (execution?.status === "SUCCEEDED") {
37
+ setStatus('success')
38
+ onSuccess?.()
39
+ } else if (execution?.status === "FAILED" || execution?.status === "TIMED_OUT") {
40
+ setStatus('error')
41
+ const errorMessage = execution.error || 'Erreur inconnue'
42
+ onError?.(errorMessage)
43
+ toast({
44
+ variant: "destructive",
45
+ title: "Erreur",
46
+ description: `La configuration a échoué: ${errorMessage}`,
47
+ })
48
+ }
49
+ } catch (error) {
50
+ console.error("Error checking execution status:", error)
51
+ }
52
+ }
53
+
54
+ const interval = setInterval(checkStatus, 2000)
55
+ return () => clearInterval(interval)
56
+ }, [executionArn, onSuccess, onError, toast])
57
+
58
+ const getStatusStyles = () => {
59
+ switch (status) {
60
+ case 'success':
61
+ return 'bg-ibm-green-10 border-ibm-green-60'
62
+ case 'error':
63
+ return 'bg-ibm-red-10 border-ibm-red-60'
64
+ default:
65
+ return 'bg-ibm-blue-10 border-ibm-blue-60'
66
+ }
67
+ }
68
+
69
+ return (
70
+ <div className={`border ${getStatusStyles()} p-4 mb-6 relative`}>
71
+ <div className="flex items-center gap-3">
72
+ <div className={`p-2 ${status === 'success' ? 'bg-ibm-green-20' : status === 'error' ? 'bg-ibm-red-20' : 'bg-ibm-blue-20'}`}>
73
+ <span className={`${status === 'running' ? 'animate-spin' : ''} ${
74
+ status === 'success' ? 'text-ibm-green-60' :
75
+ status === 'error' ? 'text-ibm-red-60' :
76
+ 'text-ibm-blue-60'
77
+ }`}>
78
+ {status === 'success' ? '✓' : status === 'error' ? '✗' : icon}
79
+ </span>
80
+ </div>
81
+ <div>
82
+ <h3 className="text-sm font-medium text-ibm-gray-100">{title}</h3>
83
+ <p className="text-xs text-ibm-gray-70 mt-0.5">{description}</p>
84
+ </div>
85
+ {onClose && (
86
+ <Button
87
+ variant="ghost"
88
+ size="xs"
89
+ className="absolute top-2 right-2"
90
+ onClick={onClose}
91
+ >
92
+ <X className="h-4 w-4" />
93
+ </Button>
94
+ )}
95
+ </div>
96
+ </div>
97
+ )
98
+ }
@@ -0,0 +1,31 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as ProgressPrimitive from "@radix-ui/react-progress"
5
+
6
+ import { cn } from "../../lib/utils"
7
+
8
+ const Progress = React.forwardRef<
9
+ React.ElementRef<typeof ProgressPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
11
+ >(({ className, value, ...props }, ref) => (
12
+ <ProgressPrimitive.Root
13
+ ref={ref}
14
+ className={cn(
15
+ "relative h-4 w-full overflow-hidden rounded-full bg-ibm-gray-30",
16
+ className
17
+ )}
18
+ {...props}
19
+ >
20
+ <ProgressPrimitive.Indicator
21
+ className="h-full w-full flex-1 transition-all"
22
+ style={{
23
+ transform: `translateX(-${100 - (value || 0)}%)`,
24
+ backgroundColor: "#0f62fe" // IBM blue
25
+ }}
26
+ />
27
+ </ProgressPrimitive.Root>
28
+ ))
29
+ Progress.displayName = ProgressPrimitive.Root.displayName
30
+
31
+ export { Progress }
@@ -0,0 +1,44 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
5
+ import { Circle } from "lucide-react"
6
+
7
+ import { cn } from "../../lib/utils"
8
+
9
+ const RadioGroup = React.forwardRef<
10
+ React.ElementRef<typeof RadioGroupPrimitive.Root>,
11
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
12
+ >(({ className, ...props }, ref) => {
13
+ return (
14
+ <RadioGroupPrimitive.Root
15
+ className={cn("grid gap-2", className)}
16
+ {...props}
17
+ ref={ref}
18
+ />
19
+ )
20
+ })
21
+ RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
22
+
23
+ const RadioGroupItem = React.forwardRef<
24
+ React.ElementRef<typeof RadioGroupPrimitive.Item>,
25
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
26
+ >(({ className, ...props }, ref) => {
27
+ return (
28
+ <RadioGroupPrimitive.Item
29
+ ref={ref}
30
+ className={cn(
31
+ "aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
32
+ className
33
+ )}
34
+ {...props}
35
+ >
36
+ <RadioGroupPrimitive.Indicator className="flex items-center justify-center">
37
+ <Circle className="h-2.5 w-2.5 fill-current text-current" />
38
+ </RadioGroupPrimitive.Indicator>
39
+ </RadioGroupPrimitive.Item>
40
+ )
41
+ })
42
+ RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
43
+
44
+ export { RadioGroup, RadioGroupItem }
@@ -0,0 +1,45 @@
1
+ "use client"
2
+
3
+ import { GripVertical } from "lucide-react"
4
+ import * as ResizablePrimitive from "react-resizable-panels"
5
+
6
+ import { cn } from "../../lib/utils"
7
+
8
+ const ResizablePanelGroup = ({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
12
+ <ResizablePrimitive.PanelGroup
13
+ className={cn(
14
+ "flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
15
+ className
16
+ )}
17
+ {...props}
18
+ />
19
+ )
20
+
21
+ const ResizablePanel = ResizablePrimitive.Panel
22
+
23
+ const ResizableHandle = ({
24
+ withHandle,
25
+ className,
26
+ ...props
27
+ }: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
28
+ withHandle?: boolean
29
+ }) => (
30
+ <ResizablePrimitive.PanelResizeHandle
31
+ className={cn(
32
+ "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
33
+ className
34
+ )}
35
+ {...props}
36
+ >
37
+ {withHandle && (
38
+ <div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
39
+ <GripVertical className="h-2.5 w-2.5" />
40
+ </div>
41
+ )}
42
+ </ResizablePrimitive.PanelResizeHandle>
43
+ )
44
+
45
+ export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
@@ -0,0 +1,227 @@
1
+ "use client"
2
+
3
+ import { Label } from "./label"
4
+ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "./select"
5
+ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./accordion"
6
+ import { Cpu, MemoryStick } from "lucide-react"
7
+ import { cpuOptions, memoryOptions, replicasOptions, ResourceOption } from "../../app/applications/models"
8
+
9
+ export interface ResourceConfig {
10
+ cpu: string
11
+ memory: string
12
+ }
13
+
14
+ export interface ScalabilityConfig {
15
+ replicas: number
16
+ minReplicas: number
17
+ maxReplicas: number
18
+ scalingStrategy: "manual" | "auto"
19
+ }
20
+
21
+ interface ResourceSettingsProps {
22
+ resources: ResourceConfig
23
+ onResourcesChange: (config: ResourceConfig) => void
24
+ scalability: ScalabilityConfig
25
+ onScalabilityChange: (config: ScalabilityConfig) => void
26
+ configuring?: boolean
27
+ }
28
+
29
+ export function ResourceSettings({
30
+ resources,
31
+ onResourcesChange,
32
+ scalability,
33
+ onScalabilityChange,
34
+ configuring = false
35
+ }: ResourceSettingsProps) {
36
+
37
+ const handleCpuChange = (value: string) => {
38
+ onResourcesChange({
39
+ ...resources,
40
+ cpu: value
41
+ })
42
+ }
43
+
44
+ const handleMemoryChange = (value: string) => {
45
+ onResourcesChange({
46
+ ...resources,
47
+ memory: value
48
+ })
49
+ }
50
+
51
+ return (
52
+ <div className="space-y-6">
53
+
54
+ <div className="space-y-2">
55
+ {/* CPU Selection */}
56
+ <div>
57
+ <Label className="text-sm font-medium text-ibm-gray-100 mb-3 block">
58
+ <Cpu className="w-4 h-4 inline mr-2" />
59
+ CPU Allocation *
60
+ </Label>
61
+ <Select
62
+ value={resources.cpu}
63
+ onValueChange={handleCpuChange}
64
+ disabled={configuring}
65
+ >
66
+ <SelectTrigger className="bg-white border-ibm-gray-20">
67
+ <SelectValue placeholder="Select CPU allocation" />
68
+ </SelectTrigger>
69
+ <SelectContent>
70
+ {cpuOptions.map((option: ResourceOption) => (
71
+ <SelectItem key={option.value} value={option.value}>
72
+ <div className="flex flex-col">
73
+ <span className="font-medium text-left">{option.label}</span>
74
+ <span className="text-xs text-ibm-gray-70 ">{option.description}</span>
75
+ </div>
76
+ </SelectItem>
77
+ ))}
78
+ </SelectContent>
79
+ </Select>
80
+ </div>
81
+
82
+ {/* Memory Selection */}
83
+ <div>
84
+ <Label className="text-sm font-medium text-ibm-gray-100 mb-3 block">
85
+ <MemoryStick className="w-4 h-4 inline mr-2" />
86
+ Memory Allocation *
87
+ </Label>
88
+ <Select
89
+ value={resources.memory}
90
+ onValueChange={handleMemoryChange}
91
+ disabled={configuring}
92
+ >
93
+ <SelectTrigger className="bg-white border-ibm-gray-20">
94
+ <SelectValue placeholder="Select memory allocation" />
95
+ </SelectTrigger>
96
+ <SelectContent>
97
+ {memoryOptions.map((option: ResourceOption) => (
98
+ <SelectItem key={option.value} value={option.value}>
99
+ <div className="flex flex-col">
100
+ <span className="font-medium text-left">{option.label}</span>
101
+ <span className="text-xs text-ibm-gray-70">{option.description}</span>
102
+ </div>
103
+ </SelectItem>
104
+ ))}
105
+ </SelectContent>
106
+ </Select>
107
+ </div>
108
+ </div>
109
+
110
+ {/* Scalability Accordion */}
111
+ <Accordion type="single" collapsible className="w-full">
112
+ <AccordionItem value="scalability" className="border-none">
113
+ <AccordionTrigger className="py-2 hover:no-underline">
114
+ <div className="flex items-center space-x-2 mt-2">
115
+ <MemoryStick className="h-4 w-4 text-ibm-gray-70" />
116
+ <span className="text-sm font-medium text-gray-700">Scaling Settings </span>
117
+ </div>
118
+ </AccordionTrigger>
119
+ <AccordionContent className="pt-2">
120
+ <div className="space-y-4 pl-6">
121
+ {/* Scaling Strategy */}
122
+ <div className="space-y-3">
123
+ <Label className="text-sm font-medium text-gray-700">Scaling Strategy</Label>
124
+ <Select
125
+ value={scalability.scalingStrategy}
126
+ onValueChange={(value) => onScalabilityChange({ ...scalability, scalingStrategy: value as "manual" | "auto" })}
127
+ disabled={configuring}
128
+ >
129
+ <SelectTrigger className="bg-white border-ibm-gray-20">
130
+ <SelectValue placeholder="Select scaling strategy" />
131
+ </SelectTrigger>
132
+ <SelectContent>
133
+ <SelectItem value="manual">
134
+ <div className="flex flex-col">
135
+ <span className="font-medium text-left">Manual</span>
136
+ <span className="text-xs text-ibm-gray-70">Fixed number of replicas</span>
137
+ </div>
138
+ </SelectItem>
139
+ <SelectItem value="auto">
140
+ <div className="flex flex-col">
141
+ <span className="font-medium text-left">Auto Scaling</span>
142
+ <span className="text-xs text-ibm-gray-70">Automatic scaling based on metrics</span>
143
+ </div>
144
+ </SelectItem>
145
+ </SelectContent>
146
+ </Select>
147
+ </div>
148
+
149
+ {/* Replicas Configuration - Strategy Dependent */}
150
+ <div className="space-y-3">
151
+ <Label className="text-sm font-medium text-gray-700">Replicas Configuration</Label>
152
+
153
+ {/* Manual Strategy */}
154
+ {scalability.scalingStrategy === "manual" && (
155
+ <div>
156
+ <Label className="text-xs text-ibm-gray-70">Fixed Replicas</Label>
157
+ <Select
158
+ value={scalability.replicas.toString()}
159
+ onValueChange={(value) => onScalabilityChange({ ...scalability, replicas: parseInt(value) })}
160
+ disabled={configuring}
161
+ >
162
+ <SelectTrigger className="bg-white border-ibm-gray-20 h-8">
163
+ <SelectValue />
164
+ </SelectTrigger>
165
+ <SelectContent>
166
+ {replicasOptions.map((option: ResourceOption) => (
167
+ <SelectItem key={option.value} value={option.value.toString()}>
168
+ {option.label}
169
+ </SelectItem>
170
+ ))}
171
+ </SelectContent>
172
+ </Select>
173
+ <p className="text-xs text-gray-500 mt-1">Fixed number of replicas that will not change automatically</p>
174
+ </div>
175
+ )}
176
+
177
+ {/* Auto Scaling Strategy */}
178
+ {scalability.scalingStrategy === "auto" && (
179
+ <div className="grid grid-cols-2 gap-4">
180
+ <div>
181
+ <Label className="text-xs text-ibm-gray-70">Min Replicas</Label>
182
+ <Select
183
+ value={scalability.minReplicas.toString()}
184
+ onValueChange={(value) => onScalabilityChange({ ...scalability, minReplicas: parseInt(value) })}
185
+ disabled={configuring}
186
+ >
187
+ <SelectTrigger className="bg-white border-ibm-gray-20 h-8">
188
+ <SelectValue />
189
+ </SelectTrigger>
190
+ <SelectContent>
191
+ {replicasOptions.map((option: ResourceOption) => (
192
+ <SelectItem key={option.value} value={option.value.toString()}>
193
+ {option.label}
194
+ </SelectItem>
195
+ ))}
196
+ </SelectContent>
197
+ </Select>
198
+ </div>
199
+ <div>
200
+ <Label className="text-xs text-ibm-gray-70">Max Replicas</Label>
201
+ <Select
202
+ value={scalability.maxReplicas.toString()}
203
+ onValueChange={(value) => onScalabilityChange({ ...scalability, maxReplicas: parseInt(value) })}
204
+ disabled={configuring}
205
+ >
206
+ <SelectTrigger className="bg-white border-ibm-gray-20 h-8">
207
+ <SelectValue />
208
+ </SelectTrigger>
209
+ <SelectContent>
210
+ {replicasOptions.map((option: ResourceOption) => (
211
+ <SelectItem key={option.value} value={option.value.toString()}>
212
+ {option.label}
213
+ </SelectItem>
214
+ ))}
215
+ </SelectContent>
216
+ </Select>
217
+ </div>
218
+ </div>
219
+ )}
220
+ </div>
221
+ </div>
222
+ </AccordionContent>
223
+ </AccordionItem>
224
+ </Accordion>
225
+ </div>
226
+ )
227
+ }
@@ -0,0 +1,48 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
5
+
6
+ import { cn } from "../../lib/utils"
7
+
8
+ const ScrollArea = React.forwardRef<
9
+ React.ElementRef<typeof ScrollAreaPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
11
+ >(({ className, children, ...props }, ref) => (
12
+ <ScrollAreaPrimitive.Root
13
+ ref={ref}
14
+ className={cn("relative overflow-hidden", className)}
15
+ {...props}
16
+ >
17
+ <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
18
+ {children}
19
+ </ScrollAreaPrimitive.Viewport>
20
+ <ScrollBar />
21
+ <ScrollAreaPrimitive.Corner />
22
+ </ScrollAreaPrimitive.Root>
23
+ ))
24
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
25
+
26
+ const ScrollBar = React.forwardRef<
27
+ React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
28
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
29
+ >(({ className, orientation = "vertical", ...props }, ref) => (
30
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
31
+ ref={ref}
32
+ orientation={orientation}
33
+ className={cn(
34
+ "flex touch-none select-none transition-colors",
35
+ orientation === "vertical" &&
36
+ "h-full w-2.5 border-l border-l-transparent p-[1px]",
37
+ orientation === "horizontal" &&
38
+ "h-2.5 flex-col border-t border-t-transparent p-[1px]",
39
+ className
40
+ )}
41
+ {...props}
42
+ >
43
+ <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
44
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
45
+ ))
46
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
47
+
48
+ export { ScrollArea, ScrollBar }
@@ -0,0 +1,26 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Search } from "lucide-react"
5
+ import { Input } from "./input"
6
+ import { cn } from "../../lib/utils"
7
+
8
+ interface SearchInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
9
+ containerClassName?: string
10
+ }
11
+
12
+ export function SearchInput({ className, containerClassName, ...props }: SearchInputProps) {
13
+ return (
14
+ <div className={cn("relative", containerClassName)}>
15
+ <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-text-secondary h-4 w-4" />
16
+ <Input
17
+ type="text"
18
+ className={cn(
19
+ "pl-10 h-10 text-base border-ui-border bg-white",
20
+ className
21
+ )}
22
+ {...props}
23
+ />
24
+ </div>
25
+ )
26
+ }