1- import { Card , CardBody , Button } from '@heroui/react' ;
1+ import { Card , CardBody , Button , Dropdown , DropdownTrigger , DropdownMenu , DropdownItem , Input } from '@heroui/react' ;
22import { t } from 'i18next' ;
3- import React , { useCallback } from 'react' ;
3+ import React , { useCallback , useState , useEffect } from 'react' ;
44import { useDropzone } from 'react-dropzone' ;
55
66import LocalIcon from '@/components/LocalIcon' ;
7- import { importOpenAPI } from '@/services/api' ;
7+ import { importOpenAPI , getTenants } from '@/services/api' ;
88import { toast } from "@/utils/toast.ts" ;
9+ import type { Tenant } from '@/types/gateway' ;
910
1011interface OpenAPIImportProps {
1112 onSuccess ?: ( ) => void ;
1213}
1314
1415const OpenAPIImport : React . FC < OpenAPIImportProps > = ( { onSuccess } ) => {
16+ const [ showAdvanced , setShowAdvanced ] = useState ( false ) ;
17+ const [ tenants , setTenants ] = useState < Tenant [ ] > ( [ ] ) ;
18+ const [ selectedTenant , setSelectedTenant ] = useState < string > ( '' ) ;
19+ const [ prefix , setPrefix ] = useState ( '' ) ;
20+ const [ loadingTenants , setLoadingTenants ] = useState ( false ) ;
21+
22+ useEffect ( ( ) => {
23+ if ( showAdvanced && tenants . length === 0 ) {
24+ setLoadingTenants ( true ) ;
25+ getTenants ( )
26+ . then ( ( data ) => setTenants ( data ) )
27+ . catch ( ( ) => toast . error ( t ( 'errors.fetch_tenants' ) ) )
28+ . finally ( ( ) => setLoadingTenants ( false ) ) ;
29+ }
30+ } , [ showAdvanced , tenants . length ] ) ;
31+
1532 const onDrop = useCallback ( async ( acceptedFiles : globalThis . File [ ] ) => {
1633 if ( acceptedFiles . length === 0 ) {
1734 toast . error ( t ( 'errors.invalid_openapi_file' ) , {
@@ -21,7 +38,11 @@ const OpenAPIImport: React.FC<OpenAPIImportProps> = ({ onSuccess }) => {
2138 }
2239
2340 try {
24- await importOpenAPI ( acceptedFiles [ 0 ] ) ;
41+ // Find the selected tenant object
42+ const tenantObj = tenants . find ( ( t : Tenant ) => t . id . toString ( ) === selectedTenant ) ;
43+ // Use tenant.prefix if available, otherwise empty string
44+ const tenantPrefix = tenantObj ? tenantObj . prefix : '' ;
45+ await importOpenAPI ( acceptedFiles [ 0 ] , tenantPrefix , prefix ) ;
2546 toast . success ( t ( 'errors.import_openapi_success' ) , {
2647 duration : 3000 ,
2748 } ) ;
@@ -31,7 +52,7 @@ const OpenAPIImport: React.FC<OpenAPIImportProps> = ({ onSuccess }) => {
3152 duration : 3000 ,
3253 } )
3354 }
34- } , [ onSuccess ] ) ;
55+ } , [ onSuccess , selectedTenant , prefix , tenants ] ) ;
3556
3657 const { getRootProps, getInputProps, isDragActive } = useDropzone ( {
3758 onDrop,
@@ -52,15 +73,15 @@ const OpenAPIImport: React.FC<OpenAPIImportProps> = ({ onSuccess }) => {
5273 isDragActive ? 'bg-primary/10 border-primary' : 'bg-content2 border-divider'
5374 } `}
5475 >
55- < input { ...getInputProps ( ) } />
76+ < input { ...getInputProps ( ) } style = { { display : 'none' } } />
5677 < LocalIcon icon = "lucide:upload" className = "text-4xl mb-4 text-primary" />
5778 { isDragActive ? (
5879 < p className = "text-lg text-primary" > Drop the OpenAPI specification file here...</ p >
5980 ) : (
6081 < div className = "text-center" >
6182 < p className = "text-lg" > Drag and drop an OpenAPI specification file here</ p >
6283 < p className = "text-sm text-default-500 mt-2" > or</ p >
63- < Button color = "primary" variant = "flat" className = "mt-2" >
84+ < Button color = "primary" variant = "flat" className = "mt-2" onClick = { e => { e . stopPropagation ( ) ; document . querySelector < HTMLInputElement > ( 'input[type="file"]' ) ?. click ( ) ; } } >
6485 Select a file
6586 </ Button >
6687 </ div >
@@ -69,6 +90,34 @@ const OpenAPIImport: React.FC<OpenAPIImportProps> = ({ onSuccess }) => {
6990 Supported formats: JSON (.json), YAML (.yaml, .yml)
7091 </ p >
7192 </ div >
93+ < div className = "mt-4 w-full flex flex-col items-center" >
94+ < Button size = "sm" variant = "light" onClick = { ( ) => setShowAdvanced ( ( v ) => ! v ) } >
95+ { showAdvanced ? t ( 'common.hide_advanced_options' , 'Hide Advanced Options' ) : t ( 'common.show_advanced_options' , 'Show Advanced Options' ) }
96+ </ Button >
97+ { showAdvanced && (
98+ < div className = "w-full mt-4 flex flex-col gap-4 items-center" >
99+ < div className = "w-full max-w-xs" >
100+ < label className = "block text-sm font-medium mb-1" > { t ( 'gateway.tenant' , 'Tenant' ) } </ label >
101+ < Dropdown isDisabled = { loadingTenants || tenants . length === 0 } className = "w-full" >
102+ < DropdownTrigger >
103+ < Button variant = "bordered" className = "w-full" >
104+ { selectedTenant ? tenants . find ( t => t . id . toString ( ) === selectedTenant ) ?. name : t ( 'gateway.select_tenant' , 'Select Tenant' ) }
105+ </ Button >
106+ </ DropdownTrigger >
107+ < DropdownMenu aria-label = "Tenant List" selectionMode = "single" selectedKeys = { selectedTenant ? [ selectedTenant ] : [ ] } onAction = { key => setSelectedTenant ( key as string ) } >
108+ { tenants . map ( tenant => (
109+ < DropdownItem key = { tenant . id . toString ( ) } > { tenant . name } </ DropdownItem >
110+ ) ) }
111+ </ DropdownMenu >
112+ </ Dropdown >
113+ </ div >
114+ < div className = "w-full max-w-xs" >
115+ < label className = "block text-sm font-medium mb-1" > { t ( 'gateway.prefix' , 'Prefix' ) } </ label >
116+ < Input value = { prefix } onChange = { e => setPrefix ( e . target . value ) } placeholder = { t ( 'gateway.prefix_placeholder' , 'Enter prefix (optional)' ) } />
117+ </ div >
118+ </ div >
119+ ) }
120+ </ div >
72121 </ CardBody >
73122 </ Card >
74123 ) ;
0 commit comments