署名記録SignedRecordBeta
署名後に本文をロックし、修正を追記として残す監査向けの記録コンポーネントです。
プレビュー
下書き
状態とバリエーション
下書き
本文を編集し、本文が入力されている間は署名できます。
下書き
署名済み
署名後は本文がロックされ、修正は追記で残します。
署名・確定済署名: 医師 山田・2026-07-02 10:30
確定済みの記録は変更できません。修正は追記で行います。
退院時説明を実施。次回外来は7月10日。
追記
- 医師 山田・2026-07-02 11:15/ 電話確認本人確認済み。退院後の連絡先を追記しました。
署名不可
本文が空の間だけ署名ボタンを無効化し、ボタンのツールチップで復帰条件を示します。
下書き
複数署名
requiredSigners を渡すと複数署名モードに。全員が署名するまで下書きのまま編集でき、最後の署名でロックします。当事者ごとの署名状況と進捗(N/M)を表示。上のボタンで操作中の当事者を切り替えて試せます(同じ人は2回署名できません)。
操作中の当事者
下書き署名 0/2
- 医師 山田未署名
- 看護師 佐藤未署名
プロパティ
表は横にスクロールできます
| プロパティ | 型 | 初期値 | 説明 |
|---|---|---|---|
| value | SignedRecordValue | - | status / signedBy / signedAt / addenda を含む controlled state です。 |
| onChange | (value: SignedRecordValue) => void | - | 署名または追記時に呼ばれます。 |
| children | ({ readOnly }) => ReactNode | - | 本文の render prop です。確定後は readOnly が true になります。 |
| signerId | string | - | 署名者と追記者として記録されるIDです。 |
| requiredSigners | { id: string; label?: ReactNode }[] | - | 複数署名モードを有効化します。全員が署名するまで draft のまま(編集可)で、最後の署名でロック。signerId は当事者のうち1人として1回だけ署名できます。未指定なら単一署名(1回の署名で即ロック)。(#259) |
| value.signatures | { signerId: string; at: string }[] | - | 複数署名モードで集まった署名。全 requiredSigners が揃うとロックします。 |
| canSign | boolean | true | 署名できる状態かどうかを渡します。 |
| cannotSignReason | ReactNode | - | 署名ボタンが無効な理由と復帰条件をツールチップで表示します。 |
| requireAddendumReason | boolean | true | 追記時の理由入力を必須にします。 |
| formatTime | (iso: string) => ReactNode | - | ISO タイムスタンプの表示を整形します。関数propのため Client Component からのみ渡すこと(Server Component から渡すと next build が落ちる)。RSC 安全な整形には timeFormat を使う。 |
| timeFormat | Intl.DateTimeFormatOptions | - | シリアライズ可能な時刻フォーマット=formatTime の RSC 安全な代替(例: { dateStyle: "short", timeStyle: "short" })。new Date(iso) に en-US ロケール固定で適用。formatTime 指定時は無視。(#338) |
| labels | SignedRecordLabels | - | バッジ、ボタン、追記フォームの文言を差し替えます。 |
使い方
import * as React from "react";
import { SignedRecord, Textarea, type SignedRecordValue } from "@gunjo/ui";
export function DischargeRecord() {
const [record, setRecord] = React.useState<SignedRecordValue>({ status: "draft", addenda: [] });
const [body, setBody] = React.useState("退院時説明を実施。次回外来は7月10日。");
const canSign = body.trim().length > 0;
return (
<div className="w-full max-w-xl rounded-lg border bg-card p-4">
<SignedRecord
value={record}
onChange={setRecord}
signerId="医師 山田"
canSign={canSign}
cannotSignReason="記録本文を入力すると署名できます。"
formatTime={(iso) => iso.slice(0, 16).replace("T", " ")}
>
{({ readOnly }) =>
readOnly ? (
<p className="whitespace-pre-wrap rounded-md border bg-muted/30 px-3 py-2 text-sm leading-6 text-foreground">
{body}
</p>
) : (
<Textarea
rows={3}
value={body}
onChange={(event) => setBody(event.target.value)}
placeholder="記録本文を入力"
aria-label="記録本文"
/>
)
}
</SignedRecord>
</div>
);
}使用コンポーネント
関連コンポーネント
設計の判断(UIXHERO)
「いつ・なぜ使うか」の判断は、姉妹サイト UIXHERO の記事で解説しています。