음성 재생과 운영 배포 정리

This commit is contained in:
Yun Chan 2026-06-28 12:18:20 +09:00
parent 8ed185ce6c
commit ac7db95542
1020 changed files with 46863 additions and 2175 deletions

View file

@ -0,0 +1,176 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { Button, Icon } from "../components/ui";
import { roleHomePath, useAuth } from "../lib/auth";
export default function PendingApproval() {
const navigate = useNavigate();
const { user, logout, refresh } = useAuth();
const [checking, setChecking] = useState(false);
const checkAgain = async () => {
if (checking) return;
setChecking(true);
try {
const nextUser = await refresh();
if (nextUser?.accountStatus === "approved") {
navigate(
nextUser.onboardingCompletedAt == null ? "/onboarding" : roleHomePath(nextUser.role),
{ replace: true },
);
}
} finally {
setChecking(false);
}
};
const signOut = async () => {
await logout();
navigate("/login", { replace: true });
};
const statusText = user?.accountStatus === "suspended" ? "접속 보류" : "승인 대기";
return (
<>
<style>{PENDING_APPROVAL_CSS}</style>
<main className="pa-page" aria-label="계정 승인 대기">
<section className="pa-panel">
<div className="pa-mark" aria-hidden="true">
<Icon name="shield" size={30} strokeWidth={1.9} />
</div>
<p className="pa-kicker">{statusText}</p>
<h1> .</h1>
<p className="pa-copy">
{user?.email ? <b>{user.email}</b> : "현재 계정"} .
Vignette .
</p>
<div className="pa-status" role="status">
<span aria-hidden="true" />
<div>
<b> </b>
<small> .</small>
</div>
</div>
<div className="pa-actions">
<Button
type="button"
size="lg"
leading={<Icon name="settings" size={16} />}
onClick={() => void checkAgain()}
disabled={checking}
>
{checking ? "확인 중" : "승인 상태 새로고침"}
</Button>
<Button
type="button"
size="lg"
variant="secondary"
leading={<Icon name="logout" size={16} />}
onClick={() => void signOut()}
>
</Button>
</div>
</section>
</main>
</>
);
}
const PENDING_APPROVAL_CSS = `
.pa-page{
min-height:100dvh;
display:grid;
place-items:center;
padding:clamp(18px,5vw,56px);
background:var(--bg-app);
color:var(--text-body);
}
.pa-panel{
width:min(100%,620px);
display:grid;
gap:var(--sp-4);
padding:clamp(24px,5vw,44px);
border:1px solid var(--border-subtle);
border-radius:var(--radius);
background:color-mix(in srgb,var(--bg-surface) 92%,white 8%);
box-shadow:0 18px 50px rgba(28,43,40,.10);
}
.pa-mark{
width:62px;
height:62px;
display:grid;
place-items:center;
border-radius:var(--radius);
background:var(--accent-tint);
color:var(--accent-deep);
border:1px solid color-mix(in srgb,var(--accent) 20%,transparent);
}
.pa-kicker{
margin:0;
color:var(--accent-deep);
font-size:12px;
font-weight:820;
}
.pa-panel h1{
margin:0;
color:var(--text-strong);
font-size:clamp(30px,5vw,46px);
line-height:1.17;
letter-spacing:0;
}
.pa-copy{
margin:0;
color:var(--text-muted);
font-size:15px;
line-height:1.7;
}
.pa-copy b{
color:var(--text-strong);
font-weight:760;
}
.pa-status{
min-width:0;
display:grid;
grid-template-columns:auto minmax(0,1fr);
gap:12px;
align-items:center;
padding:14px;
border:1px solid var(--border-subtle);
border-radius:var(--radius);
background:var(--bg-surface-2);
}
.pa-status > span{
width:10px;
height:10px;
border-radius:50%;
background:#d89a2b;
box-shadow:0 0 0 5px rgba(216,154,43,.14);
}
.pa-status b{
display:block;
color:var(--text-strong);
font-size:14px;
}
.pa-status small{
display:block;
margin-top:3px;
color:var(--text-muted);
font-size:12.5px;
line-height:1.45;
}
.pa-actions{
display:flex;
flex-wrap:wrap;
gap:10px;
}
@media (max-width:560px){
.pa-panel{
border-radius:var(--radius);
}
.pa-actions .vg-btn{
width:100%;
}
}
`;