feat: add removable sample vertical contract fixture
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { deriveAsyncState } from "../../application/view-models/async-state.js";
|
||||
import { AsyncSurface } from "../../presentation/components/async-surface.jsx";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* loading: boolean,
|
||||
* resources?: Array<{resourceId: string, title: string, createdAtLabel: string | null}>,
|
||||
* failure?: import("../../contracts/errors.js").ApiFailure
|
||||
* }} SamplePageState
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* facade: ReturnType<typeof import("./sample-facade.js").createSampleFacade>
|
||||
* }} props
|
||||
*/
|
||||
export function SampleResourcePage({ facade }) {
|
||||
const [result, setResult] = useState(
|
||||
/** @type {SamplePageState} */ ({
|
||||
loading: true,
|
||||
resources: undefined,
|
||||
failure: undefined,
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
void facade.listResources().then((outcome) => {
|
||||
if (!active) return;
|
||||
setResult(
|
||||
outcome.ok
|
||||
? { loading: false, resources: outcome.value, failure: undefined }
|
||||
: { loading: false, resources: undefined, failure: outcome.error },
|
||||
);
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [facade]);
|
||||
|
||||
const state = deriveAsyncState({
|
||||
isInitialLoading: result.loading,
|
||||
data: result.resources,
|
||||
failure: result.failure,
|
||||
});
|
||||
|
||||
return (
|
||||
<main>
|
||||
<h1>샘플 리소스</h1>
|
||||
<AsyncSurface state={state}>
|
||||
<ul>
|
||||
{(result.resources ?? []).map((resource) => (
|
||||
<li key={resource.resourceId}>{resource.title}</li>
|
||||
))}
|
||||
</ul>
|
||||
</AsyncSurface>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user