11"use client"
22
3- import { useRef , useState } from "react"
4- import { Button } from "@/components/ui/button"
5- import { Card , CardDescription , CardHeader , CardTitle } from "@/components/ui/card"
6- import { AlertCircle , FileJson , FileSymlink } from "lucide-react"
7- import { captureException } from "@sentry/nextjs"
8- import { Alert , AlertDescription , AlertTitle } from "@/components/ui/alert"
9- import { ParsedSwaggerResult , parseSwagger } from "@/app/(layout)/services/import/parseSwagger" ;
10- import { parsePostman } from "@/app/(layout)/services/import/parsePostman" ;
11-
3+ import type React from "react"
4+
5+ import { useRef , useState } from "react"
6+ import { Button } from "@/components/ui/button"
7+ import { Card , CardDescription , CardHeader , CardTitle , CardContent } from "@/components/ui/card"
8+ import { AlertCircle , FileJson , FileSymlink , Search } from "lucide-react"
9+ import { captureException } from "@sentry/nextjs"
10+ import { Alert , AlertDescription , AlertTitle } from "@/components/ui/alert"
11+ import { type ParsedSwaggerResult , parseSwagger } from "@/app/(layout)/services/import/parseSwagger"
12+ import { parsePostman } from "@/app/(layout)/services/import/parsePostman"
13+ import { Input } from "@/components/ui/input"
14+ import Image from "next/image"
15+ import { apiServices } from "@/utils/data/apiServices"
1216
1317interface ImportOptionsProps {
14- onImportComplete : ( data : ParsedSwaggerResult ) => void ;
18+ onImportComplete : ( data : ParsedSwaggerResult ) => void
1519}
1620
17- export default function ImportOptions ( { onImportComplete} : ImportOptionsProps ) {
21+ export default function ImportOptions ( { onImportComplete } : ImportOptionsProps ) {
1822 const [ importMethod , setImportMethod ] = useState < "postman" | "url" | "openapi" | null > ( null )
1923 const [ isLoading , setIsLoading ] = useState ( false )
2024 const [ error , setError ] = useState < string | null > ( null )
25+ const [ searchQuery , setSearchQuery ] = useState ( "" )
2126
2227 const openApiFileRef = useRef < HTMLInputElement > ( null )
2328 const postmanFileRef = useRef < HTMLInputElement > ( null )
@@ -38,129 +43,187 @@ export default function ImportOptions({onImportComplete}: ImportOptionsProps) {
3843 }
3944 }
4045
41-
42- const processImport = async ( file : File | null , method : string | null ) => {
46+ const processImport = async ( file : File | null , method : string | null , fileContent ?: string ) => {
4347 setIsLoading ( true )
4448 setError ( null )
4549
4650 try {
4751 if ( method === "openapi" && file ) {
48- const fileText = await file . text ( ) ;
49-
50- const parsedResult = parseSwagger ( fileText ) ;
51- console . log ( 'parsedResult' )
52+ const fileText = fileContent || await file . text ( )
53+ const parsedResult = parseSwagger ( fileText )
54+ console . log ( "parsedResult" )
5255 console . log ( parsedResult )
5356 return parsedResult
54- } else if ( method === "postman" && file ) {
55-
56- const fileText = await file . text ( ) ;
57-
58- const parsedResult = parsePostman ( fileText ) ;
59-
57+ } else if ( method === "postman" && ( file || fileContent ) ) {
58+ const fileText = fileContent || await file ! . text ( )
59+ const parsedResult = parsePostman ( fileText )
6060 return parsedResult
61-
6261 } else if ( method === "url" ) {
63-
62+ // URL import logic
6463 } else {
65- throw new Error ( "Invalid import method or missing file" ) ;
64+ throw new Error ( "Invalid import method or missing file" )
6665 }
6766 } catch ( e ) {
68- captureException ( e ) ;
69- const errorMessage = e instanceof Error ? e . message : "Unknown error occurred during import" ;
70- setError ( errorMessage ) ;
71- throw e ;
67+ captureException ( e )
68+ const errorMessage = e instanceof Error ? e . message : "Unknown error occurred during import"
69+ setError ( errorMessage )
70+ throw e
7271 } finally {
73- setIsLoading ( false ) ;
72+ setIsLoading ( false )
7473 }
7574 }
7675
77- const handleImport = async ( file : File | null , method : string | null ) => {
76+ const handleImport = async ( file : File | null , method : string | null , fileContent ?: string ) => {
7877 try {
79- const data = await processImport ( file , method )
78+ const data = await processImport ( file , method , fileContent )
8079 onImportComplete ( data ! )
8180 } catch ( error ) {
8281 console . error ( "Error processing import:" , error )
8382 // Error is already set in processImport
8483 }
8584 }
8685
86+ const handleApiServiceSelect = async ( service : typeof apiServices [ 0 ] ) => {
87+ setIsLoading ( true )
88+ setError ( null )
89+
90+ try {
91+ // Fetch the Postman collection from the configUrl
92+ const response = await fetch ( service . configUrl )
93+
94+ if ( ! response . ok ) {
95+ throw new Error ( `Failed to fetch API service configuration: ${ response . statusText } ` )
96+ }
97+
98+ const collectionData = await response . text ( )
99+
100+ // Process the fetched Postman collection
101+ await handleImport ( null , "postman" , collectionData )
102+
103+ } catch ( error ) {
104+ const errorMessage = error instanceof Error ? error . message : "Failed to import API service"
105+ setError ( errorMessage )
106+ console . error ( "Error importing API service:" , error )
107+ } finally {
108+ setIsLoading ( false )
109+ }
110+ }
111+
112+ const filteredApiServices = apiServices . filter ( ( service ) =>
113+ service . name . toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) ) ,
114+ )
87115
88116 return (
89117 < div >
90- < h1 className = "text-3xl font-bold mb-8 tracking-tight" > Import endpoints</ h1 >
91118
92- < div className = "grid gap-6" >
93- { /* OpenAPI/Swagger Import Option */ }
94- < Card >
95- < div className = "flex flex-col md:flex-row" >
96- < div className = "flex-grow" >
119+ < div className = "mb-12" >
120+ < h2 className = "text-2xl font-semibold" > Import Your Endpoints</ h2 >
121+ < p className = "text-muted-foreground mb-4" >
122+ Upload your API specification files to import endpoints directly into your project.
123+ </ p >
124+
125+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
126+ < Card >
127+ < div className = "flex flex-col h-full" >
97128 < CardHeader >
98129 < div className = "flex items-center gap-4" >
99- < FileJson className = "h-10 w-10 text-primary" />
130+ < FileJson className = "h-10 w-10 text-primary" />
100131 < div >
101132 < CardTitle className = "mb-1" > OpenAPI / Swagger</ CardTitle >
102- < CardDescription > Import API endpoints from OpenAPI or Swagger specification
103- files</ CardDescription >
133+ < CardDescription > Import API endpoints from OpenAPI or Swagger specification files</ CardDescription >
104134 </ div >
105135 </ div >
106136 </ CardHeader >
137+ < div className = "mt-auto p-6 pt-0" >
138+ < Button variant = "outline" onClick = { handleOpenApiClick } disabled = { isLoading } >
139+ Select File
140+ </ Button >
141+ </ div >
107142 </ div >
108- < div className = "flex items-center p-6" >
109- < Button variant = "outline" onClick = { handleOpenApiClick } disabled = { isLoading } >
110- Select File
111- </ Button >
112- </ div >
113- </ div >
114- < input
115- type = "file"
116- ref = { openApiFileRef }
117- className = "hidden"
118- accept = ".json,.yaml,.yml"
119- onChange = { handleFileChange }
120- />
121- </ Card >
122-
123- { /* Postman Import Option */ }
124- < Card >
125- < div className = "flex flex-col md:flex-row" >
126- < div className = "flex-grow" >
143+ < input
144+ type = "file"
145+ ref = { openApiFileRef }
146+ className = "hidden"
147+ accept = ".json,.yaml,.yml"
148+ onChange = { handleFileChange }
149+ />
150+ </ Card >
151+
152+ { /* Postman Import Option */ }
153+ < Card >
154+ < div className = "flex flex-col h-full" >
127155 < CardHeader >
128156 < div className = "flex items-center gap-4" >
129- < FileSymlink className = "h-10 w-10 text-primary" />
157+ < FileSymlink className = "h-10 w-10 text-primary" />
130158 < div >
131159 < CardTitle className = "mb-1" > Postman (Experimental)</ CardTitle >
132- < CardDescription > Import API endpoints from Postman collection
133- files</ CardDescription >
160+ < CardDescription > Import API endpoints from Postman collection files</ CardDescription >
134161 </ div >
135162 </ div >
136163 </ CardHeader >
164+ < div className = "mt-auto p-6 pt-0" >
165+ < Button variant = "outline" onClick = { handlePostmanClick } disabled = { isLoading } >
166+ Select File
167+ </ Button >
168+ </ div >
137169 </ div >
138- < div className = "flex items-center p-6" >
139- < Button variant = "outline" onClick = { handlePostmanClick } disabled = { isLoading } >
140- Select File
141- </ Button >
142- </ div >
143- </ div >
144- < input
145- type = "file"
146- ref = { postmanFileRef }
147- className = "hidden"
148- accept = ".json"
149- onChange = { handleFileChange }
150- />
151- </ Card >
170+ < input type = "file" ref = { postmanFileRef } className = "hidden" accept = ".json" onChange = { handleFileChange } />
171+ </ Card >
172+ </ div >
152173
153- { /* Error Alert */ }
154174 { error && (
155- < Alert variant = "destructive" >
156- < AlertCircle className = "h-4 w-4" />
175+ < Alert variant = "destructive" className = "mt-6" >
176+ < AlertCircle className = "h-4 w-4" />
157177 < AlertTitle > Error</ AlertTitle >
158- < AlertDescription >
159- { error }
160- </ AlertDescription >
178+ < AlertDescription > { error } </ AlertDescription >
161179 </ Alert >
162180 ) }
163181 </ div >
182+
183+ < div >
184+ < h2 className = "text-2xl font-semibold" > Browse API Services</ h2 >
185+ < p className = "text-muted-foreground mb-4" > Explore and import from our collection of popular API services.</ p >
186+
187+ < div className = "relative mb-6" >
188+ < Search className = "absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
189+ < Input
190+ type = "search"
191+ placeholder = "Search API services..."
192+ className = "pl-10"
193+ value = { searchQuery }
194+ onChange = { ( e ) => setSearchQuery ( e . target . value ) }
195+ />
196+ </ div >
197+
198+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
199+ { filteredApiServices . map ( ( service ) => (
200+ < Card key = { service . name } className = "overflow-hidden" >
201+ < div className = "flex flex-col md:flex-row" >
202+ < div className = "p-6 flex items-center justify-center md:justify-start" >
203+ < Image
204+ src = { service . image || "/placeholder.svg" }
205+ alt = { `${ service . name } logo` }
206+ width = { 64 }
207+ height = { 64 }
208+ className = "rounded-md"
209+ />
210+ </ div >
211+ < CardContent className = "flex-1 p-6 pl-0" >
212+ < CardTitle className = "mb-2" > { service . name } </ CardTitle >
213+ < CardDescription className = "mb-2" > { service . description } </ CardDescription >
214+ < Button
215+ variant = "outline"
216+ onClick = { ( ) => handleApiServiceSelect ( service ) }
217+ disabled = { isLoading }
218+ >
219+ { isLoading ? "Importing..." : "Import" }
220+ </ Button >
221+ </ CardContent >
222+ </ div >
223+ </ Card >
224+ ) ) }
225+ </ div >
226+ </ div >
164227 </ div >
165228 )
166229}
0 commit comments