署名記録SignedRecordBeta

署名後に本文をロックし、修正を追記として残す監査向けの記録コンポーネントです。

プレビュー

下書き

状態とバリエーション

下書き

本文を編集し、本文が入力されている間は署名できます。

下書き

署名済み

署名後は本文がロックされ、修正は追記で残します。

署名・確定済署名: 医師 山田・2026-07-02 10:30

確定済みの記録は変更できません。修正は追記で行います。

退院時説明を実施。次回外来は7月10日。

追記

  1. 医師 山田・2026-07-02 11:15電話確認
    本人確認済み。退院後の連絡先を追記しました。

署名不可

本文が空の間だけ署名ボタンを無効化し、ボタンのツールチップで復帰条件を示します。

下書き

複数署名

requiredSigners を渡すと複数署名モードに。全員が署名するまで下書きのまま編集でき、最後の署名でロックします。当事者ごとの署名状況と進捗(N/M)を表示。上のボタンで操作中の当事者を切り替えて試せます(同じ人は2回署名できません)。

操作中の当事者
下書き署名 0/2
  • 医師 山田未署名
  • 看護師 佐藤未署名

プロパティ

表は横にスクロールできます
プロパティ初期値説明
valueSignedRecordValue-status / signedBy / signedAt / addenda を含む controlled state です。
onChange(value: SignedRecordValue) => void-署名または追記時に呼ばれます。
children({ readOnly }) => ReactNode-本文の render prop です。確定後は readOnly が true になります。
signerIdstring-署名者と追記者として記録されるIDです。
requiredSigners{ id: string; label?: ReactNode }[]-複数署名モードを有効化します。全員が署名するまで draft のまま(編集可)で、最後の署名でロック。signerId は当事者のうち1人として1回だけ署名できます。未指定なら単一署名(1回の署名で即ロック)。(#259)
value.signatures{ signerId: string; at: string }[]-複数署名モードで集まった署名。全 requiredSigners が揃うとロックします。
canSignbooleantrue署名できる状態かどうかを渡します。
cannotSignReasonReactNode-署名ボタンが無効な理由と復帰条件をツールチップで表示します。
requireAddendumReasonbooleantrue追記時の理由入力を必須にします。
formatTime(iso: string) => ReactNode-ISO タイムスタンプの表示を整形します。関数propのため Client Component からのみ渡すこと(Server Component から渡すと next build が落ちる)。RSC 安全な整形には timeFormat を使う。
timeFormatIntl.DateTimeFormatOptions-シリアライズ可能な時刻フォーマット=formatTime の RSC 安全な代替(例: { dateStyle: "short", timeStyle: "short" })。new Date(iso) に en-US ロケール固定で適用。formatTime 指定時は無視。(#338)
labelsSignedRecordLabels-バッジ、ボタン、追記フォームの文言を差し替えます。

使い方

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 の記事で解説しています。