99 lines
3.6 KiB
React
99 lines
3.6 KiB
React
import { useState } from "react";
|
|
|
|
import { deriveAsyncState } from "../../application/view-models/async-state.js";
|
|
import { createFailure } from "../../contracts/errors.js";
|
|
import {
|
|
AsyncSurface,
|
|
EmptySurface,
|
|
LoadingSurface,
|
|
TerminalErrorSurface,
|
|
} from "../components/async-surface.jsx";
|
|
import { PageHeader } from "../components/page-header.jsx";
|
|
import {
|
|
AuthRequiredSurface,
|
|
ForbiddenSurface,
|
|
NotFoundSurface,
|
|
} from "../components/state-surfaces.jsx";
|
|
import { Button } from "../components/ui/button.jsx";
|
|
import { Card } from "../components/ui/card.jsx";
|
|
|
|
export default function StateGalleryPage() {
|
|
const [lastAction, setLastAction] = useState(
|
|
"상태 화면의 작업을 선택하면 결과가 여기에 표시됩니다.",
|
|
);
|
|
const refreshingState = deriveAsyncState({
|
|
data: ["기존 데이터"],
|
|
isFetching: true,
|
|
});
|
|
|
|
return (
|
|
<section className="ui-page">
|
|
<PageHeader
|
|
eyebrow="예제"
|
|
title="화면 상태"
|
|
description="로딩, 빈 화면, 오류, 인증 필요와 권한 없음 상태가 다음 행동까지 일관되게 안내합니다."
|
|
/>
|
|
|
|
<section className="gallery-section" aria-labelledby="async-states-title">
|
|
<header className="gallery-section__header">
|
|
<h2 id="async-states-title">비동기 데이터 상태</h2>
|
|
<p>초기 로딩과 백그라운드 갱신을 구분해 기존 콘텐츠를 보존합니다.</p>
|
|
</header>
|
|
<div className="component-grid component-grid--two">
|
|
<Card title="초기 로딩">
|
|
<LoadingSurface label="예제 데이터를 불러오는 중" />
|
|
</Card>
|
|
<Card title="백그라운드 갱신">
|
|
<AsyncSurface state={refreshingState}>
|
|
<div className="state-preview-content">기존 콘텐츠는 계속 표시됩니다.</div>
|
|
</AsyncSurface>
|
|
</Card>
|
|
<Card title="빈 화면">
|
|
<EmptySurface
|
|
title="아직 표시할 항목이 없습니다."
|
|
description="첫 항목을 추가하거나 필터를 초기화할 수 있습니다."
|
|
action={
|
|
<Button onClick={() => setLastAction("빈 화면 작업을 실행했습니다.")}>
|
|
첫 작업 시작
|
|
</Button>
|
|
}
|
|
/>
|
|
</Card>
|
|
<Card title="복구 가능한 오류">
|
|
<TerminalErrorSurface
|
|
userMessageKey={
|
|
createFailure("NETWORK_UNREACHABLE", "EXAMPLE", 0)
|
|
.userMessageKey
|
|
}
|
|
action="retry"
|
|
onAction={() => setLastAction("오류 요청을 다시 시도했습니다.")}
|
|
/>
|
|
</Card>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="gallery-section" aria-labelledby="access-states-title">
|
|
<header className="gallery-section__header">
|
|
<h2 id="access-states-title">접근과 탐색 상태</h2>
|
|
<p>인증 여부와 서버 권한 결과를 서로 다른 상태로 전달합니다.</p>
|
|
</header>
|
|
<div className="component-grid component-grid--three">
|
|
<AuthRequiredSurface
|
|
onSignIn={() => setLastAction("로그인 연동 작업을 시작했습니다.")}
|
|
/>
|
|
<ForbiddenSurface
|
|
onNavigate={() => setLastAction("접근 가능한 화면으로 이동합니다.")}
|
|
/>
|
|
<NotFoundSurface
|
|
onNavigate={() => setLastAction("시작 화면으로 이동합니다.")}
|
|
/>
|
|
</div>
|
|
</section>
|
|
|
|
<output className="gallery-notice" aria-live="polite">
|
|
{lastAction}
|
|
</output>
|
|
</section>
|
|
);
|
|
}
|