You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

82 lines
2.5 KiB
TypeScript

// frontend/components/connectors/AddAccountMenu.tsx — "계정 추가" OAuth 진입(phase-16)
// providers() 로 추가 가능한 provider를 받아 메뉴로 보여주고, 선택 시 OAuth 동의 화면으로 리다이렉트.
"use client";
import { useEffect, useRef, useState } from "react";
import { Button } from "@/components/Button";
import { connectorsApi } from "@/lib/connectors/api";
import type { ConnectorProvider } from "@/lib/types";
export function AddAccountMenu({
domain,
redirectAfter,
onError,
}: {
domain: string;
redirectAfter: string;
onError?: (msg: string) => void;
}) {
const [providers, setProviders] = useState<ConnectorProvider[]>([]);
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
connectorsApi
.providers(domain)
.then(setProviders)
.catch(() => setProviders([]));
}, [domain]);
useEffect(() => {
if (!open) return;
const onDoc = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
};
document.addEventListener("mousedown", onDoc);
return () => document.removeEventListener("mousedown", onDoc);
}, [open]);
if (providers.length === 0) return null;
const connect = async (p: ConnectorProvider) => {
setOpen(false);
try {
const { authorize_url } = await connectorsApi.oauthStart(domain, p.provider, redirectAfter);
window.location.assign(authorize_url); // 외부 OAuth 동의 화면으로 이동
} catch {
onError?.(`${p.label} 연결을 시작하지 못했어요`);
}
};
return (
<div className="cn-add" ref={ref}>
<Button
variant="lime"
icon="plus"
onClick={() => setOpen((v) => !v)}
aria-haspopup="menu"
aria-expanded={open}
>
</Button>
{open && (
<div className="cn-add-menu" role="menu" aria-label="추가할 계정 제공자">
{providers.map((p) => (
<button
key={p.provider}
type="button"
role="menuitem"
className="cn-add-item"
disabled={!p.configured}
onClick={() => connect(p)}
title={p.configured ? "" : "관리자 설정이 필요해요 (client_id 미설정)"}
>
<span>{p.label}</span>
{!p.configured && <em className="cn-add-hint"> </em>}
</button>
))}
</div>
)}
</div>
);
}