Back to index

Data Access Request and Review Queue

Table
Generates documents
Data Access Request and Review Queue

Seen as a design

Take it with you

Copy and paste, or save a file and hand it over. For a machine with no network, one “Everything” file is enough.

Everything

data-access-request-queue-everything.md

Everything in this panel, including the contents behind each URL, in one Markdown file

Prompt

1.6 KB
Build an HTML document with "Data Access Request and Review Queue". This layout runs, so there is no composition to rebuild — what it needs is content.

## What this layout does
Process data access requests with classification, least privilege, purpose, approver, review date, and automatic expiry.

## What you have to supply
- Record a pseudonymous requester code, asset, classification, requested access, purpose, owner, reviewer, state, and review date.

## The shape of the input
- `title` — string — アクセス審査の要点
- `subtitle` — string? — 申請窓口・ポリシー版・対象期間
- `program` — string — データガバナンスプログラム
- `requests` — array (3–12) — 匿名依頼者コード、資産、区分、最小権限、理由、所有者、承認、状態、期限

schema.ts holds the machine-checkable validation. Ask rather than guess when an input is missing.

## When not to use it
- Use a permission matrix to compare and design baseline permissions by role.

## How to build it
Put the input in a JSON file and run:

```bash
node scripts/render.mjs data-access-request-queue <input.json> --theme ink -o out.html
```

The output is one self-contained file and fetches nothing at render time.

## The source
- https://design-vault.pages.dev/designs/data-access-request-queue/source/template.tsx
- https://design-vault.pages.dev/designs/data-access-request-queue/source/schema.ts
- https://design-vault.pages.dev/designs/data-access-request-queue/source/sample-data.json
- https://design-vault.pages.dev/designs/data-access-request-queue/sample.html

Files

Template, input schema, sample data and metadata. This document was generated from those four. The generated document

template.tsx

7.5 KB

/designs/data-access-request-queue/source/template.tsx

Show contents
import type { Props } from "./schema";

const CSS = String.raw`
.asset-data-access-request-queue{font-family:var(--font-sans);color:var(--color-primary);background:var(--color-surface);max-width:var(--container-slide);margin:auto;padding:3rem 2.5rem;line-height:1.55;font-size:var(--text-body-sm)}
.asset-data-access-request-queue *{box-sizing:border-box}.asset-data-access-request-queue header{margin-bottom:1.2rem}.asset-data-access-request-queue .eyebrow{font-family:var(--font-mono);font-size:var(--text-label);color:var(--color-tertiary);margin:0 0 .6rem}.asset-data-access-request-queue h1{font-size:var(--text-h1);line-height:1.25;max-width:28em;margin:0}.asset-data-access-request-queue .subtitle{color:var(--color-secondary);margin:.65rem 0 0}.asset-data-access-request-queue .summary{display:flex;gap:1.5rem;flex-wrap:wrap;padding:.7rem 0;border-block:1px solid var(--color-outline);margin-bottom:1rem}.asset-data-access-request-queue .summary p{margin:0;color:var(--color-secondary);font-size:var(--text-label)}.asset-data-access-request-queue .summary strong{font-family:var(--font-mono);font-size:var(--text-h3);color:var(--color-primary);margin-right:.3rem}.asset-data-access-request-queue .request{padding:.85rem 0;border-top:1px solid var(--color-outline)}.asset-data-access-request-queue .request:last-child{border-bottom:1px solid var(--color-outline)}.asset-data-access-request-queue .head{display:flex;gap:.55rem;align-items:center;flex-wrap:wrap}.asset-data-access-request-queue code{font-family:var(--font-mono);font-size:var(--text-label);color:var(--color-tertiary);overflow-wrap:anywhere}.asset-data-access-request-queue .head h2{font-size:var(--text-body-md);margin:0;flex:1}.asset-data-access-request-queue .badge{font-size:var(--text-label);border:1px solid var(--color-outline);padding:.03rem .4rem;color:var(--color-secondary)}.asset-data-access-request-queue .scope{display:flex;gap:.5rem;flex-wrap:wrap;margin-top:.35rem}.asset-data-access-request-queue .purpose{margin:.55rem 0 .2rem;font-weight:600}.asset-data-access-request-queue .justification{margin:.15rem 0;color:var(--color-secondary);overflow-wrap:anywhere}.asset-data-access-request-queue .details{display:grid;grid-template-columns:1fr 1fr;gap:.4rem 1.2rem;margin-top:.55rem;padding:.5rem .65rem;background:var(--color-surface-subtle)}.asset-data-access-request-queue .details span{display:block;font-size:var(--text-label);color:var(--color-secondary)}.asset-data-access-request-queue .details p{margin:0;overflow-wrap:anywhere}.asset-data-access-request-queue .approval{font-family:var(--font-mono);font-size:var(--text-label)}.asset-data-access-request-queue .still-svg{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
@media(max-width:680px){.asset-data-access-request-queue{padding:1.5rem 1rem}.asset-data-access-request-queue h1{font-size:var(--text-h2)}.asset-data-access-request-queue .details{grid-template-columns:minmax(0,1fr);gap:.4rem}.asset-data-access-request-queue .summary{gap:.8rem}}
@media print{.asset-data-access-request-queue{max-width:none;padding:1rem}.asset-data-access-request-queue .request{break-inside:avoid}}
`;

const statusLabel = { submitted: "申請", "owner-review": "所有者確認", "security-review": "セキュリティ審査", approved: "承認済", provisioned: "付与済", denied: "却下", revoked: "取消済" };
const classificationLabel = { public: "公開", internal: "社内", confidential: "機密", restricted: "制限" };
const accessLabel = { read: "閲覧", "limited-write": "限定編集", admin: "管理", export: "抽出" };

function Still({ data }: { data: Props }) {
  return <svg className="still-svg" data-layout-still="true" width="1280" height="720" viewBox="0 0 1280 720" aria-hidden="true">
    <rect width="1280" height="720" fill="var(--color-surface)" />
    <text x="48" y="44" fontSize="14" fontFamily="sans-serif" fill="var(--color-secondary)">{data.program}</text>
    <text x="48" y="85" fontSize="26" fontWeight="700" fontFamily="sans-serif" fill="var(--color-on-surface)">{data.title}</text>
    <text x="48" y="120" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">申請者コード · データ資産 · 機密区分 · 最小権限 · 審査状態 · 終了日</text>
    {data.requests.slice(0, 8).map((request, index) => {
      const y = 166 + index * 62;
      return <g key={request.id}>
        <line x1="48" x2="1232" y1={y - 19} y2={y - 19} stroke="var(--color-outline)" />
        <text x="52" y={y + 3} fontSize="11" fontFamily="monospace" fill="var(--color-tertiary)">{request.id}</text>
        <text x="150" y={y + 3} fontSize="12" fontWeight="700" fontFamily="sans-serif" fill="var(--color-on-surface)">{request.asset}</text>
        <text x="410" y={y + 3} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">{classificationLabel[request.classification]} · {accessLabel[request.access]}</text>
        <text x="675" y={y + 3} fontSize="11" fontFamily="sans-serif" fill="var(--color-on-surface)">{request.requesterId} · {request.owner}</text>
        <text x="1020" y={y + 3} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">{statusLabel[request.status]} · {request.expiry ?? "期限未設定"}</text>
      </g>;
    })}
    <text x="48" y="704" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">{data.requests.length}申請 · 承認・終了日つきの最小権限付与</text>
  </svg>;
}

export default function Template(data: Props) {
  const pending = data.requests.filter((request) => ["submitted", "owner-review", "security-review"].includes(request.status)).length;
  const provisioned = data.requests.filter((request) => request.status === "provisioned").length;
  const restricted = data.requests.filter((request) => request.classification === "restricted").length;
  return <section className="asset asset-data-access-request-queue">
    <style>{CSS}</style>
    <header>
      <p className="eyebrow">{data.program} · データアクセス審査</p>
      <h1>{data.title}</h1>
      {data.subtitle && <p className="subtitle">{data.subtitle}</p>}
    </header>
    <div className="summary" aria-label="申請状況"><p><strong>{data.requests.length}</strong>申請</p><p><strong>{pending}</strong>審査中</p><p><strong>{provisioned}</strong>付与済</p><p><strong>{restricted}</strong>制限区分</p></div>
    {data.requests.map((request) => <article className="request" key={request.id}>
      <div className="head"><code>{request.id}</code><h2>{request.asset}</h2><span className="badge" data-status={request.status}>{statusLabel[request.status]}</span></div>
      <div className="scope"><span className="badge" data-classification={request.classification}>{classificationLabel[request.classification]} · {request.classification}</span><span className="badge" data-access={request.access}>権限: {accessLabel[request.access]} · {request.access}</span></div>
      <p className="purpose">{request.purpose}</p><p className="justification">{request.justification}</p>
      <div className="details">
        <div><span>依頼者コード・所属</span><p>{request.requesterId} · {request.requesterTeam}</p></div>
        <div><span>資産責任者・審査者</span><p>{request.owner} · {request.reviewer}</p></div>
        <div><span>再審査日・アクセス終了日</span><p>{request.reviewDate} · {request.expiry ?? "未設定"}</p></div>
        <div><span>承認記録</span><p className="approval">{request.approvalRef ?? "審査完了後に記録"}</p></div>
      </div>
    </article>)}
    <Still data={data} />
  </section>;
}

schema.ts

2.6 KB

/designs/data-access-request-queue/source/schema.ts

Show contents
import { z } from "zod";

const text = z.string().trim().min(1).max(160);
const short = z.string().trim().min(1).max(48);
const date = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "日付はYYYY-MM-DD形式にすること");
const requestSchema = z.object({
  id: short.describe("アクセス依頼の一意な識別子"),
  requesterId: short.describe("氏名や連絡先を含めない依頼者コード"),
  requesterTeam: short.describe("依頼チーム"),
  asset: short.describe("要求するデータ資産またはシステム"),
  classification: z.enum(["public", "internal", "confidential", "restricted"]).describe("資産の機密区分"),
  access: z.enum(["read", "limited-write", "admin", "export"]).describe("求める最小アクセス範囲"),
  purpose: short.describe("業務上の利用目的"),
  justification: text.describe("アクセスが必要な理由と対象範囲"),
  owner: short.describe("データ資産の責任者"),
  reviewer: short.describe("アクセス判断者"),
  approvalRef: short.optional().describe("承認記録の参照"),
  reviewDate: date.describe("申請内容またはアクセスの再審査日"),
  expiry: date.optional().describe("付与アクセスの終了日"),
  status: z.enum(["submitted", "owner-review", "security-review", "approved", "provisioned", "denied", "revoked"]).describe("申請から付与・取消までの状態"),
}).strict();

export const schema = z.object({
  title: text.describe("アクセス審査の要点"),
  subtitle: text.optional().describe("申請窓口・ポリシー版・対象期間"),
  program: short.describe("対象データガバナンスプログラム"),
  requests: z.array(requestSchema).min(3).max(12).describe("最小範囲・期間限定のデータアクセス申請"),
}).strict().superRefine((data, ctx) => {
  const ids = data.requests.map(({ id }) => id);
  if (new Set(ids).size !== ids.length) {
    ctx.addIssue({ code: "custom", path: ["requests"], message: "申請IDは一意にすること" });
  }
  data.requests.forEach((request, index) => {
    if (["approved", "provisioned", "revoked"].includes(request.status) && (!request.approvalRef || !request.expiry)) {
      ctx.addIssue({ code: "custom", path: ["requests", index], message: "承認済み・付与済み申請には承認記録と終了日が必要" });
    }
    if (request.expiry && request.expiry < request.reviewDate) {
      ctx.addIssue({ code: "custom", path: ["requests", index, "expiry"], message: "アクセス終了日は再審査日以後にすること" });
    }
  });
});

export type Props = z.infer<typeof schema>;

meta.yaml

1.8 KB

/designs/data-access-request-queue/source/meta.yaml

Show contents
id: data-access-request-queue
name:
  ja: データアクセス申請・審査キュー
  en: Data Access Request and Review Queue
purpose:
  ja: データアクセス申請を機密区分・最小権限・業務理由・承認者・再審査日・自動終了日とともに処理する。
  en: Process data access requests with classification, least privilege, purpose, approver, review date, and automatic expiry.
status: published
still: ./still.png
category: governance
catalogOrder: 865
jobs:
  - assign-governance
  - prioritize-diagnose
useCases:
  - status
  - requirements
layoutKinds:
  - table
  - stack
tags:
  - data-access-request
  - least-privilege
  - approval-workflow
  - time-bound-access
  - responsive
whenToUse:
  - ja: 申請ごとに必要最小限の権限を審査し、期限と承認記録を追跡するとき。
    en: Use to review least-privilege access per request and track its expiry and approval evidence.
avoidWhen:
  - ja: 役割ごとの標準権限そのものを比較・設計する場合は、権限マトリクスを使う。
    en: Use a permission matrix to compare and design baseline permissions by role.
requiredInputs:
  - ja: 匿名依頼者コード、資産、機密区分、必要な権限、利用目的、資産責任者、審査者、状態、再審査日を記録する。
    en: Record a pseudonymous requester code, asset, classification, requested access, purpose, owner, reviewer, state, and review date.
relatedLayouts:
  - permission-matrix
  - security-exception-register
dataSchema:
  title: string — アクセス審査の要点
  subtitle: string? — 申請窓口・ポリシー版・対象期間
  program: string — データガバナンスプログラム
  requests: array (3–12) — 匿名依頼者コード、資産、区分、最小権限、理由、所有者、承認、状態、期限
sources: []

sample-data.json

2.0 KB

/designs/data-access-request-queue/source/sample-data.json

Show contents
{
  "title": "機密顧客データの期限付き閲覧を承認し、管理者権限の申請は差し戻す",
  "subtitle": "Data Access Governance · Q3 2026 · weekly review queue",
  "program": "Northstar Data Stewardship",
  "requests": [
    { "id": "DAR-218", "requesterId": "USR-7F21", "requesterTeam": "Risk Analytics", "asset": "customer-risk-view", "classification": "confidential", "access": "read", "purpose": "Quarterly risk review", "justification": "Read-only access for the approved 2026-Q3 portfolio analysis; no row-level export is required.", "owner": "Customer Data Steward", "reviewer": "Risk Data Approver", "approvalRef": "GOV-APR-218", "reviewDate": "2026-10-01", "expiry": "2026-12-31", "status": "provisioned" },
    { "id": "DAR-221", "requesterId": "USR-90AC", "requesterTeam": "Product Insights", "asset": "checkout-events", "classification": "internal", "access": "read", "purpose": "Funnel investigation", "justification": "Compare retry behavior for the current checkout experiment; aggregate access is sufficient.", "owner": "Commerce Data Owner", "reviewer": "Product Data Steward", "reviewDate": "2026-09-29", "status": "owner-review" },
    { "id": "DAR-226", "requesterId": "USR-4D31", "requesterTeam": "Platform Support", "asset": "identity-admin-console", "classification": "restricted", "access": "admin", "purpose": "Support investigation", "justification": "The submitted scope is broader than the stated support task and has no time-bound incident reference.", "owner": "Identity Platform Owner", "reviewer": "Security Access Board", "reviewDate": "2026-09-27", "status": "denied" },
    { "id": "DAR-229", "requesterId": "USR-25B8", "requesterTeam": "Finance Operations", "asset": "invoice-aging-mart", "classification": "confidential", "access": "export", "purpose": "Month-end close", "justification": "A controlled extract is required to reconcile open invoices; export is limited to the approved finance workspace.", "owner": "Revenue Data Owner", "reviewer": "Finance Data Approver", "reviewDate": "2026-09-30", "status": "submitted" }
  ]
}