ApiKeyQRCodeModal.tsx 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. import { useEffect } from 'react';
  2. import { QRCodeSVG } from 'qrcode.react';
  3. import { X, AlertTriangle } from 'lucide-react';
  4. import { useTranslation } from 'react-i18next';
  5. import { Button } from './Button';
  6. import { buildApiKeyQrPayload } from '../utils/apiKeyQr';
  7. interface ApiKeyQRCodeModalProps {
  8. /** Raw API key string (only available in-memory right after creation). */
  9. apiKey: string;
  10. /** Base URL a client uses to reach Bambuddy. Defaults to the current origin. */
  11. baseUrl?: string;
  12. onClose: () => void;
  13. }
  14. export function ApiKeyQRCodeModal({ apiKey, baseUrl, onClose }: ApiKeyQRCodeModalProps) {
  15. const { t } = useTranslation();
  16. const origin = baseUrl ?? window.location.origin;
  17. const payload = buildApiKeyQrPayload(origin, apiKey);
  18. // Close on Escape key
  19. useEffect(() => {
  20. const handleKeyDown = (e: KeyboardEvent) => {
  21. if (e.key === 'Escape') onClose();
  22. };
  23. window.addEventListener('keydown', handleKeyDown);
  24. return () => window.removeEventListener('keydown', handleKeyDown);
  25. }, [onClose]);
  26. return (
  27. <div
  28. className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4"
  29. onClick={onClose}
  30. >
  31. <div
  32. className="bg-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary w-full max-w-sm"
  33. onClick={(e) => e.stopPropagation()}
  34. >
  35. {/* Header */}
  36. <div className="flex items-center justify-between px-6 py-4 border-b border-bambu-dark-tertiary">
  37. <h2 className="text-lg font-semibold text-white">{t('settings.apiKeyQrTitle')}</h2>
  38. <button
  39. onClick={onClose}
  40. className="text-bambu-gray hover:text-white transition-colors"
  41. aria-label={t('common.close')}
  42. >
  43. <X className="w-5 h-5" />
  44. </button>
  45. </div>
  46. {/* Content */}
  47. <div className="p-6 flex flex-col items-center">
  48. <p className="text-sm text-bambu-gray mb-4 text-center">
  49. {t('settings.apiKeyQrCaption')}
  50. </p>
  51. <div className="bg-white p-4 rounded-lg mb-4">
  52. <QRCodeSVG value={payload} size={256} />
  53. </div>
  54. <div className="flex items-start gap-2 text-xs text-amber-700 dark:text-amber-400 mb-4">
  55. <AlertTriangle className="w-4 h-4 flex-shrink-0 mt-0.5" />
  56. <span>{t('settings.apiKeyQrWarning')}</span>
  57. </div>
  58. <Button onClick={onClose} className="w-full">
  59. {t('common.close')}
  60. </Button>
  61. </div>
  62. </div>
  63. </div>
  64. );
  65. }