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.

120 lines
3.8 KiB
TypeScript

// frontend/components/settings/AccountTab.tsx — 프로필(이름·역할) 편집 + 로그인 정보
"use client";
import { useEffect, useState } from "react";
import { Avatar } from "@/components/Avatar";
import { Button } from "@/components/Button";
import { Icon } from "@/components/Icon";
import { logout } from "@/lib/auth";
import { settingsApi } from "@/lib/settings/api";
import { useMe } from "@/lib/hooks/useSettings";
import type { ToastFn } from "./SettingsClient";
const AUTH_ENABLED = process.env.NEXT_PUBLIC_AUTH_ENABLED === "true";
export function AccountTab({ toast }: { toast: ToastFn }) {
const { data: me, isLoading, refresh } = useMe();
const [name, setName] = useState("");
const [role, setRole] = useState("");
const [saving, setSaving] = useState(false);
useEffect(() => {
if (me) {
setName(me.name);
setRole(me.role === "member" || me.role === "admin" ? me.role : me.role || "");
}
}, [me]);
const dirty = me ? name.trim() !== me.name || role.trim() !== (me.role ?? "") : false;
const onSave = async () => {
if (!name.trim()) {
toast("이름을 입력해 주세요", "amber");
return;
}
setSaving(true);
try {
await settingsApi.updateProfile({ name: name.trim(), role: role.trim() });
await refresh();
toast("프로필을 저장했어요", "green");
} catch {
toast("저장에 실패했어요", "coral");
} finally {
setSaving(false);
}
};
const onLogout = async () => {
await logout();
if (typeof window !== "undefined") window.location.assign("/login");
};
return (
<section className="set-sec" aria-label="계정">
<header className="set-sechead">
<h2></h2>
<p> · .</p>
</header>
<div className="set-card set-profile">
<Avatar initial={me?.initial || "지"} tone="blue" label={me?.name} />
<div className="sp-meta">
<b>{me?.name ?? (isLoading ? "불러오는 중…" : "지우")}</b>
<span className="sp-email">
<Icon name="mail" />
{me?.email || "이메일 미설정"}
</span>
</div>
<span className="sp-role" data-role={me?.role}>
{me?.role === "admin" ? "관리자" : me?.role || "멤버"}
</span>
</div>
<div className="set-card">
<div className="set-field">
<label htmlFor="acc-name"></label>
<input
id="acc-name"
className="set-input"
value={name}
maxLength={40}
onChange={(e) => setName(e.target.value)}
placeholder="표시 이름"
/>
</div>
<div className="set-field">
<label htmlFor="acc-role"> / </label>
<input
id="acc-role"
className="set-input"
value={role}
maxLength={40}
onChange={(e) => setRole(e.target.value)}
placeholder="예: 프로덕트 디자이너"
/>
</div>
<div className="set-actions">
<Button variant="lime" icon="tick" onClick={onSave} disabled={!dirty || saving}>
{saving ? "저장 중…" : "변경사항 저장"}
</Button>
</div>
</div>
<div className="set-card set-row-between">
<div className="sp-meta">
<b> </b>
<span className="set-hint">
{AUTH_ENABLED
? "현재 기기에서 로그인되어 있어요."
: "데모 모드 — 인증이 꺼져 있어요(AUTH_ENABLED=false)."}
</span>
</div>
{AUTH_ENABLED && (
<Button variant="glass" icon="back" onClick={onLogout}>
</Button>
)}
</div>
</section>
);
}