chore: 年度リストを昇順に変更

This commit is contained in:
admin
2026-06-07 21:58:20 +09:00
parent 91632ea5b6
commit 4a500ff5e9
25 changed files with 1531 additions and 67 deletions

View File

@@ -279,6 +279,72 @@
background: #f3eeff;
color: #6f42c1;
}
/* テーブル */
.tbl-wrap {
overflow-x: auto;
margin-top: 12px;
}
table.apply-tbl {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
table.apply-tbl th,
table.apply-tbl td {
border: 1px solid #dee2e6;
padding: 5px 8px;
white-space: nowrap;
vertical-align: top;
}
table.apply-tbl th {
background: #f1f3f5;
font-weight: 700;
}
table.apply-tbl tr:hover td {
background: #f8f9fa;
}
.proc-select {
width: 100%;
padding: 8px 10px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 13px;
margin-bottom: 10px;
}
.arrive-badge {
font-weight: 700;
color: #0a3622;
background: #d1e7dd;
padding: 2px 8px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
cursor: pointer;
}
.info-box {
background: #fff3cd;
border: 1px solid #ffc107;
border-radius: 6px;
padding: 10px 14px;
font-size: 12px;
line-height: 1.7;
margin-bottom: 14px;
}
.env-badge {
display: inline-block;
padding: 2px 10px;
border-radius: 10px;
font-size: 11px;
font-weight: 700;
}
.env-sandbox {
background: #fff3cd;
color: #856404;
}
.env-production {
background: #d1e7dd;
color: #0a3622;
}
</style>
</head>
<body>
@@ -422,6 +488,193 @@
</div>
</div>
<!-- ─── 申請書作成(最終確認試験用) ───────────────────── -->
<div class="card" id="applyCard">
<h2>📝 テスト申請書作成(最終確認試験用)</h2>
<p class="desc">
検証環境へテスト手続を送信して到達番号を取得します。<br />
取得した到達番号は試験データ表と Excel
に記入してデジタル庁へ送付してください。
</p>
<div class="info-box">
⚠️ ログイン後に操作可能です。<br />
環境:
<span id="envLabel" class="env-badge env-sandbox">sandbox検証</span>
&nbsp; ソフトウェアID:
<code id="swIdLabel" style="font-size: 11px"></code>
</div>
<div class="form-group">
<label>手続識別子(対象手続を選択)</label>
<select class="proc-select" id="procSelect">
<optgroup label="電子送達関係">
<option value="900A013800001000">
900A013800001000 APIテスト用手続電子送達0001電子送達1件
</option>
</optgroup>
<optgroup label="社会保険関係 標準形式">
<option value="950A010002010000">
950A010002010000 APIテスト社保通1007 公文書(署名あり)
</option>
<option value="950A010002011000">
950A010002011000 APIテスト社保通1008 公文書(署名なし)
</option>
<option value="950A010700001000">
950A010700001000 APIテスト社保通1001 手続終了
</option>
<option value="950A010700002000">
950A010700002000 APIテスト社保通1002 手続終了
</option>
<option value="950A010700005000">
950A010700005000 APIテスト社保通1003 取下げ承認
</option>
<option value="950A010700006000">
950A010700006000 APIテスト社保通1004 取下げ却下
</option>
</optgroup>
<optgroup label="労働保険関係 標準形式">
<option value="950A010200003000">
950A010200003000 APIテスト労保通1001 手続終了
</option>
<option value="950A010200004000">
950A010200004000 APIテスト労保通1002 手続終了
</option>
<option value="950A010002012000">
950A010002012000 APIテスト労保通1004 コメント(メッセージ)
</option>
<option value="950A010002016000">
950A010002016000 APIテスト労保通1008 コメント(公文書)
</option>
</optgroup>
<optgroup label="社会保険関係 個別署名形式">
<option value="950A101220029000">
950A101220029000 APIテスト社保個1001 手続終了
</option>
<option value="950A102200038000">
950A102200038000 APIテスト社保個1005 コメント(メッセージ)
</option>
<option value="950A102200039000">
950A102200039000 APIテスト社保個1006 公文書(署名あり)
</option>
</optgroup>
<optgroup label="その他">
<option value="900A020700013000">
900A020700013000 APIテスト労保適用0003 補正申請
</option>
<option value="900A102800072000">
900A102800072000 APIテスト労保適用個0014 プレ印字
</option>
</optgroup>
</select>
</div>
<div
style="
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 14px;
"
>
<button
class="btn btn-primary"
id="applyBtn"
onclick="doTestApply()"
style="background: #6f42c1"
>
📤 テスト申請を送信
</button>
<span
id="applySpinner"
style="display: none; font-size: 13px; color: #555"
>⏳ 送信中...</span
>
</div>
<!-- 結果テーブル -->
<div id="applyResult" style="display: none">
<div class="dl-result" style="margin-bottom: 10px">
<span>✅ 到達番号: </span>
<strong
id="arriveIdResult"
class="arrive-badge"
title="クリックでコピー"
onclick="copyArriveId()"
></strong>
<span
style="font-size: 12px; color: #6c757d; margin-left: 8px"
id="arriveDateResult"
></span>
</div>
<div style="font-size: 12px; color: #555" id="procNameResult"></div>
</div>
<div
id="applyError"
style="
display: none;
color: #842029;
font-size: 13px;
margin-top: 8px;
padding: 8px 12px;
background: #f8d7da;
border-radius: 4px;
"
></div>
</div>
<!-- ─── 申請案件一覧 ─────────────────────────────────────── -->
<div class="card" id="listCard">
<h2>📋 申請案件一覧(到達番号確認)</h2>
<p class="desc">
送信済みの申請案件一覧を表示します。到達番号を Excel
に転記してデジタル庁へ提出してください。
</p>
<div
style="display: flex; gap: 10px; margin-bottom: 12px; flex-wrap: wrap"
>
<button class="btn btn-secondary" onclick="loadApplyList()">
🔄 一覧を更新
</button>
<button
class="btn"
onclick="exportApplyListCsv()"
style="background: #0d6efd; color: #fff"
>
📥 CSV エクスポート
</button>
</div>
<div
id="listStatus"
style="font-size: 13px; color: #6c757d; margin-bottom: 8px"
></div>
<div class="tbl-wrap" id="applyListWrap">
<table class="apply-tbl">
<thead>
<tr>
<th>#</th>
<th>到達番号</th>
<th>到達日時</th>
<th>手続識別子</th>
<th>手続名</th>
<th>ステータス</th>
<th>サブステータス</th>
</tr>
</thead>
<tbody id="applyListBody">
<tr>
<td
colspan="7"
style="text-align: center; color: #aaa; padding: 20px"
>
「一覧を更新」ボタンで取得してください
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script>
let timer = null;
let currentTab = "api";
@@ -677,10 +930,174 @@
const r = await fetch("/egov/status");
const d = await r.json();
apply(d);
// 環境・ソフトウェアID を表示
document.getElementById("envLabel").textContent =
d.environment === "sandbox"
? "sandbox検証環境"
: "production本番環境";
document.getElementById("envLabel").className =
"env-badge " +
(d.environment === "sandbox" ? "env-sandbox" : "env-production");
if (!["idle", "logged_in", "error"].includes(d.status))
startPolling();
} catch (_) {}
// ソフトウェアID 表示
try {
const r2 = await fetch("/egov/config-info");
if (r2.ok) {
const cfg = await r2.json();
document.getElementById("swIdLabel").textContent =
cfg.software_id || "—";
}
} catch (_) {}
})();
// ─── テスト申請書作成 ────────────────────────────────────
let _applyResults = [];
async function doTestApply() {
const procId = document.getElementById("procSelect").value;
const applyBtn = document.getElementById("applyBtn");
const spinner = document.getElementById("applySpinner");
const resultDiv = document.getElementById("applyResult");
const errorDiv = document.getElementById("applyError");
applyBtn.disabled = true;
show("applySpinner");
resultDiv.style.display = "none";
errorDiv.style.display = "none";
try {
const res = await fetch("/egov/test-apply", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ proc_id: procId }),
});
const data = await res.json();
if (!res.ok) {
errorDiv.textContent =
"エラー: " + (data.detail || JSON.stringify(data));
errorDiv.style.display = "block";
return;
}
document.getElementById("arriveIdResult").textContent =
data.arrive_id || "(取得中)";
document.getElementById("arriveDateResult").textContent =
data.arrive_date ? ` 到達: ${data.arrive_date}` : "";
document.getElementById("procNameResult").textContent =
data.proc_name || "";
resultDiv.style.display = "block";
// 送信済みリストに追加
_applyResults.unshift({
arrive_id: data.arrive_id,
arrive_date: data.arrive_date,
proc_id: procId,
proc_name: data.proc_name,
status: "",
sub_status: "",
});
renderApplyList(_applyResults);
} catch (err) {
errorDiv.textContent = "通信エラー: " + err.message;
errorDiv.style.display = "block";
} finally {
applyBtn.disabled = false;
hide("applySpinner");
}
}
function copyArriveId() {
const val = document.getElementById("arriveIdResult").textContent;
navigator.clipboard
.writeText(val)
.then(() => alert("コピーしました: " + val))
.catch(() => {});
}
// ─── 申請案件一覧 ────────────────────────────────────────
async function loadApplyList() {
const statusDiv = document.getElementById("listStatus");
statusDiv.textContent = "⏳ 一覧を取得中...";
try {
const res = await fetch("/egov/apply-list");
if (!res.ok) {
const e = await res.json();
statusDiv.textContent = "エラー: " + (e.detail || "不明");
return;
}
const data = await res.json();
_applyResults = data.apply_list || [];
renderApplyList(_applyResults);
statusDiv.textContent = `${data.total}`;
} catch (err) {
statusDiv.textContent = "通信エラー: " + err.message;
}
}
function renderApplyList(items) {
const tbody = document.getElementById("applyListBody");
if (!items.length) {
tbody.innerHTML =
'<tr><td colspan="7" style="text-align:center;color:#aaa;padding:20px;">申請案件はありません</td></tr>';
return;
}
tbody.innerHTML = items
.map(
(it, i) => `
<tr>
<td>${i + 1}</td>
<td><span class="arrive-badge" onclick="navigator.clipboard.writeText('${esc(it.arrive_id)}')" title="クリックでコピー">${esc(it.arrive_id)}</span></td>
<td>${esc(it.arrive_date)}</td>
<td style="font-family:monospace;font-size:11px">${esc(it.proc_id)}</td>
<td>${esc(it.proc_name)}</td>
<td>${esc(it.status)}</td>
<td>${esc(it.sub_status)}</td>
</tr>
`,
)
.join("");
}
function esc(s) {
return String(s || "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
}
function exportApplyListCsv() {
if (!_applyResults.length) {
alert("データがありません");
return;
}
const header = [
"到達番号",
"到達日時",
"手続識別子",
"手続名",
"ステータス",
"サブステータス",
];
const rows = _applyResults.map((it) =>
[
it.arrive_id,
it.arrive_date,
it.proc_id,
it.proc_name,
it.status,
it.sub_status,
]
.map((v) => `"${String(v || "").replace(/"/g, '""')}"`)
.join(","),
);
const csv = "\uFEFF" + [header.join(","), ...rows].join("\r\n");
const link = document.createElement("a");
link.href = URL.createObjectURL(new Blob([csv], { type: "text/csv" }));
link.download = "egov_apply_list.csv";
link.click();
}
</script>
</body>
</html>