Skip to main content
Glama
APIKeyGenerateModal.tsx7.55 kB
'use client' import { Fragment, useState } from 'react' import { Dialog, Transition } from '@headlessui/react' import APIKeyDisplay from './APIKeyDisplay' interface APIKeyGenerateModalProps { isOpen: boolean onClose: () => void onGenerate: () => Promise<{ success: boolean; apiKey?: string; error?: string }> } type ModalState = 'initial' | 'generating' | 'success' | 'error' /** * Modal for generating new API keys * Shows the full API key only once with security warnings */ export default function APIKeyGenerateModal({ isOpen, onClose, onGenerate }: APIKeyGenerateModalProps) { const [state, setState] = useState<ModalState>('initial') const [apiKey, setApiKey] = useState<string>('') const [error, setError] = useState<string>('') const handleGenerate = async () => { setState('generating') setError('') try { const result = await onGenerate() if (result.success && result.apiKey) { setApiKey(result.apiKey) setState('success') } else { setError(result.error || 'Failed to generate API key') setState('error') } } catch (err) { setError('An unexpected error occurred') setState('error') } } const handleClose = () => { // Reset state when closing setState('initial') setApiKey('') setError('') onClose() } return ( <Transition appear show={isOpen} as={Fragment}> <Dialog as="div" className="relative z-50" onClose={handleClose}> <Transition.Child as={Fragment} enter="ease-out duration-300" enterFrom="opacity-0" enterTo="opacity-100" leave="ease-in duration-200" leaveFrom="opacity-100" leaveTo="opacity-0" > <div className="fixed inset-0 bg-black bg-opacity-25" /> </Transition.Child> <div className="fixed inset-0 overflow-y-auto"> <div className="flex min-h-full items-center justify-center p-4 text-center"> <Transition.Child as={Fragment} enter="ease-out duration-300" enterFrom="opacity-0 scale-95" enterTo="opacity-100 scale-100" leave="ease-in duration-200" leaveFrom="opacity-100 scale-100" leaveTo="opacity-0 scale-95" > <Dialog.Panel className="w-full max-w-2xl transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all"> <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900" > {state === 'success' ? 'API Key Generated' : 'Generate New API Key'} </Dialog.Title> <div className="mt-4"> {/* Initial State */} {state === 'initial' && ( <div className="space-y-4"> <p className="text-sm text-gray-500"> Generate a new API key for authenticating requests to the Hostaway MCP Server. This key will be shown only once, so make sure to copy and save it securely. </p> <div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4"> <p className="text-sm text-yellow-800"> <strong>Important:</strong> The API key will be displayed only once. {`You won't be able to view it again after closing this dialog.`} </p> </div> </div> )} {/* Generating State */} {state === 'generating' && ( <div className="flex items-center justify-center py-8"> <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div> <span className="ml-3 text-sm text-gray-500">Generating API key...</span> </div> )} {/* Success State */} {state === 'success' && ( <div className="space-y-4"> <p className="text-sm text-gray-500 mb-4"> Your API key has been generated successfully. Copy it now and store it in a secure location. </p> <APIKeyDisplay apiKey={apiKey} /> </div> )} {/* Error State */} {state === 'error' && ( <div className="space-y-4"> <div className="bg-red-50 border border-red-200 rounded-lg p-4"> <div className="flex"> <div className="flex-shrink-0"> <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor"> <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" /> </svg> </div> <div className="ml-3"> <h3 className="text-sm font-medium text-red-800">Error</h3> <p className="mt-1 text-sm text-red-700">{error}</p> </div> </div> </div> </div> )} </div> {/* Footer Actions */} <div className="mt-6 flex justify-end space-x-3"> {state === 'initial' && ( <> <button type="button" className="inline-flex justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2" onClick={handleClose} > Cancel </button> <button type="button" className="inline-flex justify-center rounded-md border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2" onClick={handleGenerate} > Generate Key </button> </> )} {(state === 'success' || state === 'error') && ( <button type="button" className="inline-flex justify-center rounded-md border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2" onClick={handleClose} > {state === 'success' ? `I've saved my key` : 'Close'} </button> )} </div> </Dialog.Panel> </Transition.Child> </div> </div> </Dialog> </Transition> ) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/darrentmorgan/hostaway-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server