"use client"; import { useEffect, useRef, useState } from "react"; import { Link as LinkIcon, Briefcase, AlertTriangle, Database } from "lucide-react"; import { checkAvailability } from "@/lib/api/provisioning"; import { Field, TextInput, CheckAvailBadge, AvailStatus, availToInputStatus, } from "./fields"; /** * Step 1 · 회사 기본 정보 (시안 v2 포팅). * 좌측: 식별자 섹션 + 법인정보 섹션 (둘 다 기본 노출) * 우측: LIVE PREVIEW 카드 (접속 URL / DB 이름 / 회사 코드 / 주의) * * 로직 (유지): * - subdomain 입력 시 db_prefix / company_code 자동 제안 (사용자 수정 전까지) * - debounce 350ms 로 백엔드 /check 호출 * - valid: 3개 식별자 available + company_name 존재 */ export default function Step1Basic({ state, setState, onValidChange, }: { state: Record; setState: (patch: Record) => void; onValidChange: (valid: boolean) => void; }) { const [subStatus, setSubStatus] = useState("idle"); const [prefStatus, setPrefStatus] = useState("idle"); const [codeStatus, setCodeStatus] = useState("idle"); const timerRef = useRef(null); function onSubChange(v: string) { const sub = v.toLowerCase().replace(/[^a-z0-9-]/g, ""); const patch: Record = { subdomain: sub }; if (!state.db_prefix_manual) { patch.db_prefix = sub.replace(/-/g, "_"); } if (!state.company_code_manual) { patch.company_code = sub.toUpperCase().replace(/-/g, "_"); } setState(patch); } function onDbPrefixChange(v: string) { setState({ db_prefix: v.toLowerCase().replace(/[^a-z0-9_]/g, ""), db_prefix_manual: true }); } function onCompanyCodeChange(v: string) { setState({ company_code: v.toUpperCase().replace(/[^A-Z0-9_]/g, ""), company_code_manual: true }); } useEffect(() => { if (timerRef.current) clearTimeout(timerRef.current); setSubStatus(state.subdomain ? "checking" : "idle"); setPrefStatus(state.db_prefix ? "checking" : "idle"); setCodeStatus(state.company_code ? "checking" : "idle"); const payload = { subdomain: state.subdomain, dbPrefix: state.db_prefix, companyCode: state.company_code, }; if (!payload.subdomain && !payload.dbPrefix && !payload.companyCode) return; timerRef.current = setTimeout(async () => { try { const r: any = await checkAvailability(payload); if (payload.subdomain === state.subdomain) { const sub = r?.subdomain; setSubStatus( !sub ? "idle" : !sub.valid_format || sub.reserved ? "invalid" : sub.available ? "available" : "taken", ); } if (payload.dbPrefix === state.db_prefix) { const pref = r?.db_prefix; setPrefStatus(!pref ? "idle" : !pref.valid_format ? "invalid" : pref.available ? "available" : "taken"); } if (payload.companyCode === state.company_code) { const cc = r?.company_code; setCodeStatus(!cc ? "idle" : !cc.valid_format ? "invalid" : cc.available ? "available" : "taken"); } } catch { setSubStatus("idle"); setPrefStatus("idle"); setCodeStatus("idle"); } }, 350); return () => { if (timerRef.current) clearTimeout(timerRef.current); }; }, [state.subdomain, state.db_prefix, state.company_code]); useEffect(() => { const valid = subStatus === "available" && prefStatus === "available" && codeStatus === "available" && !!state.company_name; onValidChange(valid); }, [subStatus, prefStatus, codeStatus, state.company_name, onValidChange]); return (
{/* 왼쪽: 폼 */}
01 · 기본 정보

회사 기본 정보

subdomain 을 입력하면 접속 URL 과 DB 이름이 자동 결정됩니다. 생성 후에는 변경 불가합니다.
{/* ── 식별자 ── */}
} label="식별자" hint="생성 후 변경 불가" />
} > } > } > setState({ company_name: v })} placeholder="화면·문서에 표시될 회사 이름" />
{/* ── 법인 정보 ── */}
} label="법인 정보" />
setState({ business_registration_number: v })} placeholder="xxx-xx-xxxxx" mono /> setState({ representative_name: v })} placeholder="대표자 성함" /> setState({ representative_phone: v })} placeholder="02-0000-0000" mono /> setState({ email: v })} placeholder="admin@example.com" mono /> setState({ address: v })} placeholder="도로명 주소" /> setState({ website: v })} placeholder="https://" mono />
{/* 오른쪽: LIVE PREVIEW */}
실시간 미리보기
https:// {state.subdomain || "___"} .invyone.com {state.db_prefix || "___"} _vexplor {state.company_code ? ( {state.company_code} ) : ( ___ )}
주의 — subdomain 과 db_prefix 는 생성 후 변경할 수 없습니다.
); } function SectionHead({ icon, label, hint, }: { icon: React.ReactNode; label: string; hint?: string; }) { return (
{icon} {label} {hint && ( — {hint} )}
); } function PreviewField({ label, children }: { label: string; children: React.ReactNode }) { return (
{label}
{children}
); }