mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-30 06:32:24 +00:00
89 lines
3.3 KiB
TypeScript
89 lines
3.3 KiB
TypeScript
import { useEffect, useRef } from 'react';
|
|
|
|
import { useEscapeKey } from '../../hooks/useEscapeKey';
|
|
import { useT } from '../../lib/i18n/I18nContext';
|
|
import { type AccountProvider, type ProviderDescriptor, PROVIDERS } from '../../types/accounts';
|
|
import { CloseIcon } from '../ui';
|
|
import { ProviderIcon } from './providerIcons';
|
|
|
|
interface AddAccountModalProps {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
onPick: (provider: ProviderDescriptor) => void;
|
|
/** Providers the user has already connected — filtered out of the picker. */
|
|
connectedProviders?: ReadonlySet<AccountProvider>;
|
|
}
|
|
|
|
const AddAccountModal = ({ open, onClose, onPick, connectedProviders }: AddAccountModalProps) => {
|
|
const { t } = useT();
|
|
const closeBtnRef = useRef<HTMLButtonElement | null>(null);
|
|
|
|
useEscapeKey(onClose, open);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
closeBtnRef.current?.focus();
|
|
}, [open]);
|
|
if (!open) return null;
|
|
|
|
const available = connectedProviders
|
|
? PROVIDERS.filter(p => !connectedProviders.has(p.id))
|
|
: PROVIDERS;
|
|
|
|
return (
|
|
<div
|
|
data-testid="add-account-modal"
|
|
className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-labelledby="add-account-modal-title"
|
|
onClick={onClose}>
|
|
<div
|
|
className="w-[420px] max-w-[90vw] rounded-2xl bg-white dark:bg-neutral-900 p-6 shadow-strong"
|
|
onClick={e => e.stopPropagation()}>
|
|
<div className="mb-4 flex items-center justify-between">
|
|
<h2
|
|
id="add-account-modal-title"
|
|
className="text-lg font-semibold text-stone-900 dark:text-neutral-100">
|
|
{t('accounts.addModal.title')}
|
|
</h2>
|
|
<button
|
|
ref={closeBtnRef}
|
|
type="button"
|
|
onClick={onClose}
|
|
data-analytics-id="add-account-modal-close"
|
|
className="rounded p-1 text-stone-500 dark:text-neutral-400 hover:bg-stone-100 dark:hover:bg-neutral-800 dark:bg-neutral-800 dark:hover:bg-neutral-800/60"
|
|
aria-label={t('common.close')}>
|
|
<CloseIcon className="h-5 w-5" />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
{available.length === 0 ? (
|
|
<div className="rounded-lg border border-dashed border-stone-200 dark:border-neutral-800 p-6 text-center text-sm text-stone-500 dark:text-neutral-400">
|
|
{t('accounts.addModal.allConnected')}
|
|
</div>
|
|
) : (
|
|
available.map(p => (
|
|
<button
|
|
key={p.id}
|
|
type="button"
|
|
data-analytics-id={`add-account-provider-${p.id}`}
|
|
data-testid={`add-account-provider-${p.id}`}
|
|
onClick={() => onPick(p)}
|
|
className="flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left transition-colors hover:bg-stone-100 dark:hover:bg-neutral-800 dark:bg-neutral-800 dark:hover:bg-neutral-800/60">
|
|
<ProviderIcon provider={p.id} className="h-5 w-5 flex-none" />
|
|
<span className="text-sm font-medium text-stone-900 dark:text-neutral-100">
|
|
{p.label}
|
|
</span>
|
|
</button>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default AddAccountModal;
|