@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.
- package/components/layout/index.ts +31 -0
- package/components/layout/layout-container.tsx +85 -0
- package/components/layout/root-layout-wrapper.tsx +33 -0
- package/components/layout/sidebar/data.tsx +88 -0
- package/components/layout/sidebar/main-sidebar.tsx +177 -0
- package/components/layout/sidebar/sidebar.tsx +750 -0
- package/components/layout/skeleton.tsx +15 -0
- package/components/ui/accordion.tsx +58 -0
- package/components/ui/alert-dialog.tsx +133 -0
- package/components/ui/alert.tsx +59 -0
- package/components/ui/aspect-ratio.tsx +7 -0
- package/components/ui/assets-header.tsx +50 -0
- package/components/ui/avatar.tsx +50 -0
- package/components/ui/badge.tsx +54 -0
- package/components/ui/breadcrumb.tsx +115 -0
- package/components/ui/button.tsx +83 -0
- package/components/ui/calendar.tsx +66 -0
- package/components/ui/card.tsx +79 -0
- package/components/ui/carousel.tsx +262 -0
- package/components/ui/certificate-editor.tsx +445 -0
- package/components/ui/chart.tsx +365 -0
- package/components/ui/checkbox.tsx +30 -0
- package/components/ui/collapsible.tsx +11 -0
- package/components/ui/command.tsx +153 -0
- package/components/ui/context-menu.tsx +200 -0
- package/components/ui/dialog.tsx +122 -0
- package/components/ui/drawer.tsx +118 -0
- package/components/ui/dropdown-menu.tsx +200 -0
- package/components/ui/environment-settings.tsx +173 -0
- package/components/ui/environment-variables-config.tsx +175 -0
- package/components/ui/file-import.tsx +177 -0
- package/components/ui/form.tsx +178 -0
- package/components/ui/hover-card.tsx +29 -0
- package/components/ui/index.ts +54 -0
- package/components/ui/input-otp.tsx +71 -0
- package/components/ui/input.tsx +23 -0
- package/components/ui/label.tsx +26 -0
- package/components/ui/logo.tsx +17 -0
- package/components/ui/menubar.tsx +236 -0
- package/components/ui/navigation-menu.tsx +128 -0
- package/components/ui/page-header.tsx +35 -0
- package/components/ui/pagination.tsx +112 -0
- package/components/ui/popover.tsx +31 -0
- package/components/ui/process-status.tsx +98 -0
- package/components/ui/progress.tsx +31 -0
- package/components/ui/radio-group.tsx +44 -0
- package/components/ui/resizable.tsx +45 -0
- package/components/ui/resource-settings.tsx +227 -0
- package/components/ui/scroll-area.tsx +48 -0
- package/components/ui/search-input.tsx +26 -0
- package/components/ui/secret-explorer.tsx +274 -0
- package/components/ui/secret-properties-editor.tsx +642 -0
- package/components/ui/select.tsx +162 -0
- package/components/ui/selected-asset.tsx +120 -0
- package/components/ui/separator.tsx +31 -0
- package/components/ui/sheet.tsx +140 -0
- package/components/ui/skeleton.tsx +15 -0
- package/components/ui/slider.tsx +28 -0
- package/components/ui/sonner.tsx +31 -0
- package/components/ui/switch.tsx +29 -0
- package/components/ui/table.tsx +117 -0
- package/components/ui/tabs.tsx +55 -0
- package/components/ui/textarea.tsx +22 -0
- package/components/ui/toast.tsx +131 -0
- package/components/ui/toaster.tsx +35 -0
- package/components/ui/toggle-group.tsx +61 -0
- package/components/ui/toggle.tsx +45 -0
- package/components/ui/tooltip.tsx +30 -0
- package/components/ui/user-menu.tsx +86 -0
- package/hooks/index.ts +5 -0
- package/hooks/use-auth.ts +10 -0
- package/hooks/use-mobile.ts +19 -0
- package/hooks/use-toast.ts +8 -0
- package/hooks/use-websocket.tsx +76 -0
- package/index.ts +11 -1
- package/lib/menu-utils.ts +48 -0
- 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
|
+
}
|