Files
sam-react-prod/src/components/settings/BarobillIntegration/index.tsx
김보곤 021d31b6b4 feat: [barobill] 로그인 정보 등록/수정 조건부 표시
- 바로빌 ID 등록 시 아이디 표시 + 수정 버튼
- 미등록 시 기존 등록 버튼 유지
- IntegrationStatus에 member 정보 추가
2026-02-21 14:44:55 +09:00

273 lines
10 KiB
TypeScript

'use client';
import { useState, useEffect, useCallback } from 'react';
import { PageLayout } from '@/components/organisms/PageLayout';
import { PageHeader } from '@/components/organisms/PageHeader';
import { Link2, Loader2, Pencil, Check } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { toast } from 'sonner';
import { LoginModal } from './LoginModal';
import { SignupModal } from './SignupModal';
import { BankServiceModal } from './BankServiceModal';
import {
getIntegrationStatus,
getAccountLinkUrl,
getCardLinkUrl,
getCertificateUrl,
} from './actions';
import type { IntegrationStatus } from './types';
export function BarobillIntegration() {
const [status, setStatus] = useState<IntegrationStatus | null>(null);
const [loginOpen, setLoginOpen] = useState(false);
const [signupOpen, setSignupOpen] = useState(false);
const [bankServiceOpen, setBankServiceOpen] = useState(false);
const [loadingAction, setLoadingAction] = useState<string | null>(null);
const loadStatus = useCallback(async () => {
try {
const result = await getIntegrationStatus();
if (result.success && result.data) {
setStatus(result.data);
}
} catch {
// 상태 조회 실패 시 무시 (페이지 렌더링에 영향 없음)
}
}, []);
useEffect(() => {
loadStatus();
}, [loadStatus]);
const handleExternalLink = useCallback(async (
type: 'account' | 'card' | 'certificate',
) => {
setLoadingAction(type);
try {
const actionMap = {
account: getAccountLinkUrl,
card: getCardLinkUrl,
certificate: getCertificateUrl,
};
const result = await actionMap[type]();
if (result.success && result.data?.url) {
window.open(result.data.url, '_blank');
} else {
toast.error(result.error || '서비스 페이지 URL 조회에 실패했습니다.');
}
} catch {
toast.error('서비스 페이지 URL 조회 중 오류가 발생했습니다.');
} finally {
setLoadingAction(null);
}
}, []);
const hasLogin = !!status?.member?.barobillId;
return (
<PageLayout>
<PageHeader
title="바로빌 연동 관리"
description="바로빌 청구, 장부를 연동합니다."
icon={Link2}
/>
<div className="space-y-6">
{/* 바로빌 연동 */}
<section>
<h2 className="text-lg font-semibold mb-3"> </h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* 바로빌 로그인 정보 */}
<Card>
<CardContent className="p-5">
{hasLogin ? (
<>
<h3 className="font-semibold mb-3"> </h3>
<div className="space-y-2 mb-4">
<div className="flex items-center gap-2 text-sm">
<span className="text-muted-foreground w-16 shrink-0"></span>
<span className="font-medium">{status?.member?.barobillId}</span>
</div>
<div className="flex items-center gap-2 text-sm">
<span className="text-muted-foreground w-16 shrink-0"></span>
<span className="font-medium"></span>
<Badge variant="secondary" className="text-xs">
<Check className="h-3 w-3 mr-1" />
</Badge>
</div>
</div>
<Button
variant="outline"
className="w-full"
onClick={() => setLoginOpen(true)}
>
<Pencil className="h-4 w-4 mr-2" />
</Button>
</>
) : (
<>
<h3 className="font-semibold mb-2"> ?</h3>
<p className="text-sm text-muted-foreground mb-4">
</p>
<Button
variant="destructive"
className="w-full"
onClick={() => setLoginOpen(true)}
>
</Button>
</>
)}
</CardContent>
</Card>
{/* 바로빌 회원가입 등록 */}
<Card>
<CardContent className="p-5">
<h3 className="font-semibold mb-2"> ?</h3>
<p className="text-sm text-muted-foreground mb-4">
</p>
<Button
variant="destructive"
className="w-full"
onClick={() => setSignupOpen(true)}
>
</Button>
</CardContent>
</Card>
</div>
</section>
{/* 계좌 연동 */}
<section>
<h2 className="text-lg font-semibold mb-3"> </h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* 은행 빠른조회 서비스 등록 */}
<Card>
<CardContent className="p-5">
<div className="flex items-center gap-2 mb-2">
<h3 className="font-semibold"> </h3>
{status && status.bankServiceCount > 0 && (
<Badge variant="secondary">{status.bankServiceCount}</Badge>
)}
</div>
<ul className="text-sm text-muted-foreground mb-4 space-y-1">
<li> </li>
<li> / </li>
<li> </li>
</ul>
<Button
variant="destructive"
className="w-full"
onClick={() => setBankServiceOpen(true)}
>
</Button>
</CardContent>
</Card>
{/* 계좌 연동 등록 */}
<Card>
<CardContent className="p-5">
<div className="flex items-center gap-2 mb-2">
<h3 className="font-semibold"> </h3>
{status && status.accountLinkCount > 0 && (
<Badge variant="secondary">{status.accountLinkCount}</Badge>
)}
</div>
<ul className="text-sm text-muted-foreground mb-4 space-y-1">
<li> </li>
<li> (/)</li>
</ul>
<Button
variant="destructive"
className="w-full"
onClick={() => handleExternalLink('account')}
disabled={loadingAction === 'account'}
>
{loadingAction === 'account' ? (
<><Loader2 className="h-4 w-4 mr-2 animate-spin" /> ...</>
) : '계좌 연동 등록'}
</Button>
</CardContent>
</Card>
</div>
</section>
{/* 카드 연동 & 공인인증서 등록 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* 카드 연동 */}
<section>
<h2 className="text-lg font-semibold mb-3"> </h2>
<Card>
<CardContent className="p-5">
<h3 className="font-semibold mb-2"> </h3>
<ul className="text-sm text-muted-foreground mb-4 space-y-1">
<li> </li>
<li> // </li>
</ul>
<Button
variant="destructive"
className="w-full"
onClick={() => handleExternalLink('card')}
disabled={loadingAction === 'card'}
>
{loadingAction === 'card' ? (
<><Loader2 className="h-4 w-4 mr-2 animate-spin" /> ...</>
) : '카드 연동 등록'}
</Button>
</CardContent>
</Card>
</section>
{/* 공인인증서 등록 */}
<section>
<h2 className="text-lg font-semibold mb-3"> </h2>
<Card>
<CardContent className="p-5">
<h3 className="font-semibold mb-2"> </h3>
<ul className="text-sm text-muted-foreground mb-4 space-y-1">
<li> </li>
<li> ( )</li>
</ul>
<Button
variant="destructive"
className="w-full"
onClick={() => handleExternalLink('certificate')}
disabled={loadingAction === 'certificate'}
>
{loadingAction === 'certificate' ? (
<><Loader2 className="h-4 w-4 mr-2 animate-spin" /> ...</>
) : '공인인증서 등록'}
</Button>
</CardContent>
</Card>
</section>
</div>
</div>
{/* 모달 */}
<LoginModal
open={loginOpen}
onOpenChange={setLoginOpen}
onSuccess={loadStatus}
/>
<SignupModal
open={signupOpen}
onOpenChange={setSignupOpen}
onSuccess={loadStatus}
/>
<BankServiceModal
open={bankServiceOpen}
onOpenChange={setBankServiceOpen}
/>
</PageLayout>
);
}