Skip to content

UI 設計 - 国際貨物輸送管理システム

概要

本ドキュメントでは、国際貨物輸送管理システムの UI 設計を定義する。

設計方針

OOUX(オブジェクト指向 UI 設計) をベースに、ユーザーが操作する「オブジェクト」(貨物予約・追跡・荷役・航路・請求書)を中心に画面を構成する。各画面はオブジェクトの状態を可視化し、アクターに応じた操作を提供する。

技術スタック

技術 役割
Spring Boot 4 + Thymeleaf SSR(サーバーサイドレンダリング)でフル HTML を生成
htmx 2.x フォームバリデーション・ステータス自動更新など部分的な動的更新
Bootstrap 5 レスポンシブグリッド・コンポーネント
PRG パターン フォーム送信後は必ず Redirect-Get で二重送信を防止

基本 UX 原則

  • オブジェクト中心: 一覧 → 詳細 → アクションの自然な流れ
  • 状態の可視化: BookingStatus・TransportStatus をバッジで常時表示
  • フィードバック: 操作成功・失敗はフラッシュメッセージで通知
  • アクセシビリティ: ARIA ラベル・キーボードナビゲーション対応

UI オブジェクト定義

OOUX に基づき、システム内の主要オブジェクトとそのアクション・属性を定義する。

主要オブジェクト

オブジェクト 主な属性 ユーザーアクション 関連オブジェクト
貨物予約(Booking) bookingId, 出発地, 目的地, 希望期限, 貨物種別, 重量, BookingStatus 新規登録・詳細確認・経路割り当て・キャンセル 追跡情報, 航路, 荷役履歴
追跡情報(Tracking) trackingNumber, TransportStatus, 現在地, ステータス履歴 追跡番号検索・履歴確認 貨物予約
荷役作業(HandlingEvent) eventId, 貨物 ID, 荷役種別, 場所, 実施日時, 担当者 新規登録・一覧確認 貨物予約
航路(Voyage) voyageNumber, 出発港, 到着港, 出発予定日, 到着予定日 一覧確認・経路割り当てへの提供 貨物予約
請求書(Invoice) invoiceId, 貨物予約, 金額, 割引, 消費税, PaymentStatus 一覧確認・詳細確認・支払い確認 貨物予約

オブジェクト間の関係

Booking 1 ─── 1 Tracking
Booking 1 ─── N HandlingEvent
Booking N ─── M Voyage(経路割り当てを通じて)
Booking 1 ─── 1 Invoice

画面一覧

画面名 URL パス 説明 主要アクター 対応 US
ログイン /login 認証フォーム 全ロール -
ダッシュボード / 全体サマリー・最新荷役情報 全ロール US01
貨物予約一覧 /bookings 予約済み貨物の一覧・検索 荷主、営業担当者 US02, US03
貨物予約登録 /bookings/new 新規予約フォーム 営業担当者 US04
予約詳細 /bookings/{bookingId} 予約情報・経路・荷役履歴 荷主、営業担当者 US05, US06
経路割り当て /bookings/{bookingId}/route 利用可能な航路から経路を選択 営業担当者 US07, US08, US09
貨物追跡入力 /tracking 追跡番号入力フォーム 荷主、荷受人、追跡管理者 US13
追跡詳細 /tracking/{trackingNumber} 輸送ステータス履歴タイムライン 荷主、荷受人 US14, US15
荷役作業登録 /handling/new 荷役イベント登録フォーム 荷役作業員 US10, US11
荷役作業一覧 /handling 荷役履歴一覧・検索 荷役作業員、追跡管理者 US12
航路一覧 /voyages 航路・スケジュール一覧 経路設計者 -
請求書一覧 /billing/invoices 請求書の一覧・ステータス管理 経理担当者 US16, US17
請求書詳細 /billing/invoices/{invoiceId} 請求書詳細・支払い確認 経理担当者 US18
割引ポリシー一覧 /admin/discount-policies 割引ポリシーの一覧・有効期限管理 ROLE_ADMIN US-ADM-01
割引ポリシー登録 /admin/discount-policies/new 新規割引ポリシー登録フォーム ROLE_ADMIN US-ADM-01
割引ポリシー編集 /admin/discount-policies/{id}/edit 割引ポリシー編集フォーム ROLE_ADMIN US-ADM-01
公開貨物追跡 /public/tracking/{trackingId} 認証不要の貨物状態照会ページ(荷主が URL 共有可) 荷主・荷受人(未認証) US13

共通レイアウト設計

ナビゲーション構成

全画面共通のナビゲーションバー(Bootstrap 5 navbar)を上部に配置する。ロールに応じてメニュー項目を表示制御する。

メニュー項目 遷移先 表示ロール
ダッシュボード / 全ロール
貨物予約 /bookings ROLE_SALES, ROLE_SHIPPER
貨物追跡 /tracking ROLE_SHIPPER, ROLE_CONSIGNEE, ROLE_TRACKER
荷役管理 /handling ROLE_HANDLER, ROLE_TRACKER
航路管理 /voyages ROLE_ROUTE_DESIGNER
請求管理 /billing/invoices ROLE_BILLING
管理設定 /admin/discount-policies ROLE_ADMIN
ログアウト /logout 全ロール

共通レイアウト ワイヤーフレーム

uml diagram

Bootstrap 5 グリッド運用ルール

  • コンテナ: container-fluid で横幅を最大活用
  • 一覧画面: テーブル幅 col-12
  • フォーム画面: 入力欄 col-md-8 offset-md-2(中央寄せ)
  • 詳細画面: 左カラム col-md-8、右サイドバー col-md-4
  • ブレークポイント: モバイル(< 768px)では 1 カラム積み上げ

画面遷移図

フロー全体の画面遷移を PlantUML ステートチャート図で表現する。

uml diagram


画面詳細設計

ログイン画面 (/login)

ワイヤーフレーム

uml diagram

仕様

  • Spring Security の標準ログイン画面をカスタマイズ
  • ログイン失敗時: 「ユーザー名またはパスワードが正しくありません」を赤色で表示
  • ログイン成功後: ロールに応じてダッシュボードへリダイレクト
  • セッションタイムアウト後は自動的に /login?timeout へリダイレクト

ダッシュボード (/)

ワイヤーフレーム

uml diagram

仕様

  • サマリーカード: 今月の予約件数・輸送中件数・未割り当て件数・未払い請求件数
  • 最新荷役作業: 直近 10 件を降順表示
  • ロール制御: ROLE_BILLING のみ「未払い請求」カードを表示
  • htmx: ページ初期ロード時に /api/dashboard/summaryhx-get で取得

貨物予約一覧 (/bookings)

ワイヤーフレーム

uml diagram

仕様

  • 検索フィルタ: 出発地・目的地(港コード)・BookingStatus でフィルタリング
  • ステータスバッジ: BookingStatus に応じた色分け(Bootstrap badge
  • ページネーション: 1 ページ 20 件
  • 新規登録: ROLE_SALES のみ表示
  • htmx: 検索フォームに hx-get="/bookings" hx-target="#booking-list" で部分更新

貨物予約登録 (/bookings/new)

ワイヤーフレーム

uml diagram

仕様

  • 入力項目: 出発地・目的地(UNLOCODE 形式 5 文字)・希望到着期限・貨物種別・重量
  • バリデーション: htmx で hx-post 送信前にクライアントサイドチェック、サーバー側は Bean Validation
  • 貨物種別: GENERAL_CARGO, REFRIGERATED, HAZARDOUS, PERISHABLE から選択
  • 登録成功: PRG パターンで /bookings/{bookingId} へリダイレクト
  • エラー時: 同画面を再描画し、エラーフィールドを赤ボーダーで強調

予約詳細 (/bookings/{bookingId})

ワイヤーフレーム

uml diagram

仕様

  • ステータスバッジ: ページタイトル横に BookingStatus を大きく表示
  • 経路情報: 未割り当ての場合は「経路が割り当てられていません」と表示し [経路を割り当て] を強調
  • 荷役履歴: HandlingEvent を時系列降順で表示
  • [キャンセル]: ROLE_SALES のみ表示。確認ダイアログ後に POST /bookings/{bookingId}/cancel
  • [追跡を表示]: trackingNumber が発行済みの場合のみ表示

経路割り当て (/bookings/{bookingId}/route)

ワイヤーフレーム

uml diagram

仕様

  • 航路候補: 出発地・目的地・希望期限を条件に絞り込み済みの候補を表示
  • ラジオ選択: 航路を選択すると htmx hx-get で下部の「選択中の航路詳細」を部分更新
  • 希望期限超過: 到着予定が希望期限を超える航路は アイコン付きで警告
  • 割り当て成功: PRG パターンで /bookings/{bookingId} へリダイレクト

貨物追跡入力 (/tracking)

ワイヤーフレーム

uml diagram

仕様

  • 入力フィールド: 追跡番号(TRK-YYYYMMDD-NNNN 形式)
  • バリデーション: フォーマット不正の場合はインラインエラー表示
  • 未発見: 404 の場合は「該当する貨物が見つかりません」メッセージ
  • 認証不要: 荷受人(ROLE_CONSIGNEE)は認証なしでもアクセス可

追跡詳細 (/tracking/{trackingNumber})

ワイヤーフレーム

uml diagram

仕様

  • 自動更新: htmx hx-get="/tracking/{trackingNumber}/status" hx-trigger="every 30s" hx-target="#status-timeline" で部分更新
  • タイムライン: TransportStatus の変化を時系列で表示。最新状態を最上部に
  • TransportStatus の遷移: NOT_RECEIVED → RECEIVED → LOADED → ONBOARD_CARRIER → UNLOADED → AWAITING_CLAIM → CLAIMED
  • 推定到着日: YYYY-MM-DD 頃 の形式で表示。未確定の場合は「未確定」と表示
  • CustomsStatus: PENDING(審査中)/ CLEARED(通関済)/ HELD(留置中)/ REJECTED(不可) をバッジで表示
  • EXCEPTION: 異常発生時は赤色バッジで表示し、内容を詳細表示
  • [予約詳細を表示]: ROLE_SALES, ROLE_SHIPPER のみ表示

荷役作業登録 (/handling/new)

ワイヤーフレーム

uml diagram

仕様

  • 荷役種別: RECEIVE, LOAD, UNLOAD, CUSTOMS_CLEARANCE, CLAIM から選択
  • 追跡番号: TRK-YYYYMMDD-NNNN 形式。[📷 カメラスキャン] ボタンでバーコード・QR スキャン入力に対応
  • 実施日時: 未来日時は警告表示(投機的な登録は許可)
  • 登録成功: PRG パターンで /handling へリダイレクト

荷役作業一覧 (/handling)

ワイヤーフレーム

uml diagram

仕様

  • 検索フィルタ: 貨物 ID・荷役種別・場所(港コード)でフィルタリング
  • htmx: 検索フォームに hx-get="/handling" hx-target="#handling-list" で部分更新
  • 新規登録: ROLE_HANDLER のみ表示
  • ページネーション: 1 ページ 20 件

航路一覧 (/voyages)

ワイヤーフレーム

uml diagram

仕様

  • 検索フィルタ: 出発港・到着港・出発日でフィルタリング
  • 空き状況: 積載容量に余裕があるかを「あり / なし」で表示
  • 閲覧専用: ROLE_ROUTE_DESIGNER は読み取りのみ。航路の追加・変更は管理機能から
  • 経路割り当てへの連携: 経路割り当て画面が本データを参照して候補を生成

請求書一覧 (/billing/invoices)

ワイヤーフレーム

uml diagram

仕様

  • フィルタ: PaymentStatus(PENDING, CONFIRMED, OVERDUE)・発行日でフィルタリング
  • ステータスバッジ: PENDING は赤、CONFIRMED は緑、OVERDUE は濃い赤で表示
  • 支払期限超過: 期限超過かつ未払いの場合は行を赤色ハイライト
  • アクセス制御: ROLE_BILLING のみアクセス可能

請求書詳細 (/billing/invoices/{invoiceId})

ワイヤーフレーム

uml diagram

仕様

  • 金額内訳: 基本運賃・サーチャージ・割引・消費税を明細表示
  • [支払い確認を登録]: POST /billing/invoices/{invoiceId}/confirm を送信。PRG パターンで同画面へリダイレクト
  • 確認済み: PaymentStatus が CONFIRMED の場合は支払いフォームを非表示にし、確認日時を表示
  • PDF 出力: GET /billing/invoices/{invoiceId}/pdf で請求書 PDF をダウンロード(将来実装)

割引ポリシー一覧 (/admin/discount-policies)

ワイヤーフレーム

uml diagram

仕様

  • 一覧: 有効期間・有効 / 無効ステータスでフィルタリング可能
  • [編集]: /admin/discount-policies/{id}/edit に遷移
  • [無効化]: POST /admin/discount-policies/{id}/disable で論理削除(PRG パターン)
  • [+ 新規ポリシー登録]: /admin/discount-policies/new に遷移
  • アクセス制御: ROLE_ADMIN のみアクセス可能。他ロールは 403 画面を表示

割引ポリシー登録 (/admin/discount-policies/new)

ワイヤーフレーム

uml diagram

仕様

  • バリデーション: 割引率は -50〜100% の範囲、有効開始日 ≤ 有効終了日
  • 重複チェック: 同一の「貨物種別 × 顧客区分 × 期間」のポリシーが既に存在する場合はエラー表示
  • [登録する]: POST /admin/discount-policies に送信。PRG パターンで一覧にリダイレクト
  • [キャンセル]: /admin/discount-policies に戻る

公開貨物追跡 (/public/tracking/{trackingId})

ワイヤーフレーム

uml diagram

仕様

  • 認証: 不要(Spring Security の permitAll()/public/** を許可)
  • 追跡番号フォーム: GET /public/tracking/{trackingId} でページ表示。結果は同一ページ内に表示
  • 404 処理: 存在しない追跡番号は「該当する貨物が見つかりません。追跡番号を確認の上、再度お試しください」を表示
  • 連絡先: フッターに問い合わせメールアドレスを表示(荷主への導線確保)
  • レスポンシブ: モバイルファースト(スマートフォンで QR コードから直接アクセスを想定)
  • 表示情報の制限: TransportStatus・最終イベント・現在地のみ(荷主名・住所等の個人情報は非表示)
  • AFTER_COMMIT タイムラグ: ステータス反映に最大 30 秒かかる旨を画面下部に注記する

htmx 部分更新パターン

追跡ステータス自動更新

追跡詳細画面では、荷物の状態をユーザーがリロードせずに確認できるよう、30 秒ごとに自動更新する。

<!-- 追跡詳細画面のステータスタイムライン部分 -->
<div id="status-timeline"
     hx-get="/tracking/TRK-20260328-1234/status"
     hx-trigger="every 30s"
     hx-swap="innerHTML">
  <!-- Thymeleaf でサーバーレンダリングされた初期コンテンツ -->
</div>
<p id="last-updated">最終更新: <span th:text="${lastUpdated}"></span></p>

サーバー側レスポンス: /tracking/{trackingNumber}/status は HTML フラグメントを返す(Content-Type: text/html)。

検索フォームの部分更新

貨物予約一覧・荷役作業一覧の検索フォームは、ページ全体を再読み込みせずに結果テーブルのみを更新する。

<form hx-get="/bookings"
      hx-target="#booking-list"
      hx-swap="outerHTML"
      hx-push-url="true">
  <input name="origin" type="text" placeholder="出発地">
  <input name="destination" type="text" placeholder="目的地">
  <select name="status">...</select>
  <button type="submit">検索</button>
</form>
<div id="booking-list">
  <!-- 検索結果テーブル -->
</div>

hx-push-url="true" により、検索条件が URL に反映されブラウザ履歴に残る。

経路候補の動的読み込み

経路割り当て画面でラジオボタンを選択すると、選択した航路の詳細を動的に読み込む。

<input type="radio" name="voyageNumber" value="V0042"
       hx-get="/api/voyages/V0042/detail"
       hx-target="#voyage-detail"
       hx-swap="innerHTML">

フォームのインラインバリデーション

入力フィールドからフォーカスが外れたタイミング(blur)でサーバーサイドバリデーションを実行する。

<input name="origin" type="text"
       hx-post="/api/validate/location"
       hx-trigger="blur"
       hx-target="next .error-message"
       hx-swap="innerHTML">
<span class="error-message text-danger"></span>

エラーハンドリング

バリデーションエラー表示

  • フィールドレベル: 各入力フィールドの下に赤字でメッセージ表示(Bootstrap invalid-feedback
  • フォームレベル: フォーム上部にアラートバナー(alert-danger)でまとめて表示
  • Thymeleaf: th:errors="*{fieldName}" でサーバーバリデーションエラーを表示
<div class="mb-3">
  <label for="origin" class="form-label">出発地 <span class="text-danger">*</span></label>
  <input type="text" id="origin" name="origin"
         th:class="${#fields.hasErrors('origin')} ? 'form-control is-invalid' : 'form-control'">
  <div class="invalid-feedback" th:errors="*{origin}">出発地は必須です</div>
</div>

フラッシュメッセージ

PRG パターンのリダイレクト後に、操作結果を Flash Attribute でフィードバックする。

操作 メッセージ例 Bootstrap クラス
予約登録成功 「貨物予約 BK-1234 を登録しました」 alert-success
経路割り当て成功 「経路 V0042 を割り当てました」 alert-success
荷役登録成功 「荷役作業 HE-0042 を登録しました」 alert-success
支払い確認成功 「請求書 INV-0021 の支払いを確認しました」 alert-success
バリデーションエラー 「入力内容に誤りがあります。確認してください」 alert-danger
システムエラー 「処理中にエラーが発生しました。時間をおいて再試行してください」 alert-danger

フラッシュメッセージは共通フラグメント fragments/alerts.html で一元管理する。

エラーページ

HTTP ステータス 画面 内容
400 Bad Request /error/400 不正なリクエスト。入力を確認してください
403 Forbidden /error/403 アクセス権限がありません
404 Not Found /error/404 指定されたページまたはリソースが見つかりません
500 Internal Server Error /error/500 サーバーエラーが発生しました。管理者に連絡してください

各エラーページはナビゲーションバーを表示し、ダッシュボードへ戻るリンクを提供する。

htmx エラーハンドリング

htmx リクエストがエラーを返した場合は、htmx:responseError イベントをキャッチして通知を表示する。

document.addEventListener('htmx:responseError', function(event) {
  const status = event.detail.xhr.status;
  const messageEl = document.getElementById('htmx-error-toast');
  if (status === 404) {
    messageEl.textContent = '対象データが見つかりませんでした';
  } else {
    messageEl.textContent = '通信エラーが発生しました。再試行してください';
  }
  messageEl.classList.remove('d-none');
});

アクセシビリティ

キーボードナビゲーション

  • Tab 順序: フォーム項目 → 送信ボタン → ナビゲーションの順に自然な Tab 移動
  • Enter キー: フォーム内でのフォーカス状態でEnterキーを押すと送信
  • Escape キー: モーダル・確認ダイアログを閉じる
  • スキップリンク: <a href="#main-content" class="visually-hidden-focusable">コンテンツにスキップ</a> をヘッダー先頭に配置

ARIA 対応

要素 ARIA 属性
ナビゲーションバー role="navigation" aria-label="メインナビゲーション"
検索フォーム role="search" aria-label="貨物検索"
データテーブル role="grid" aria-label="[テーブル名]"
ステータスバッジ aria-label="ステータス: ROUTE_PROPOSED"
ローディングインジケーター aria-live="polite" aria-busy="true"
エラーメッセージ role="alert" aria-live="assertive"
フラッシュメッセージ role="status" aria-live="polite"

htmx と ARIA

htmx の部分更新後に動的コンテンツが更新されることをスクリーンリーダーに通知する。

<!-- 自動更新エリアは aria-live="polite" で通知 -->
<div id="status-timeline"
     aria-live="polite"
     aria-atomic="false"
     hx-get="/tracking/TRK-20260328-1234/status"
     hx-trigger="every 30s">
</div>

カラーコントラスト

  • 通常テキスト: コントラスト比 4.5:1 以上(WCAG AA 準拠)
  • 大きいテキスト(18px 以上): 3:1 以上
  • ステータスバッジは色のみに依存せず、テキストラベルを必ず併記

付録: BookingStatus / TransportStatus 対応表

BookingStatus バッジ定義

ステータス 表示ラベル Bootstrap クラス 意味
PRELIMINARY 仮予約 badge bg-warning text-dark 経路未割り当て
ROUTE_PROPOSED 経路提案済 badge bg-primary 経路割り当て完了・未確認
CONFIRMED 確認済 badge bg-success 予約確定
TRACKING_ISSUED 追跡番号発行済 badge bg-info text-dark 追跡番号付与
IN_TRANSIT 輸送中 badge bg-primary 積み込み済・輸送中
DELIVERED 配送完了 badge bg-success 配達完了
SETTLED 精算完了 badge bg-secondary 請求・支払い完了
CANCELLED キャンセル badge bg-danger キャンセル済

TransportStatus バッジ定義

ステータス 表示ラベル Bootstrap クラス
NOT_RECEIVED 未受取 badge bg-secondary
RECEIVED 受取済 badge bg-info text-dark
LOADED 積み込み済 badge bg-primary
ONBOARD_CARRIER 搭載中 badge bg-primary
UNLOADED 荷降ろし済 badge bg-warning text-dark
AWAITING_CLAIM 引取待ち badge bg-warning text-dark
CLAIMED 引取完了 badge bg-success
EXCEPTION 例外 badge bg-danger