From 8d0cc78abc258dd62ce25d1bebfa6ceabdcadcd3 Mon Sep 17 00:00:00 2001 From: hyunho Date: Mon, 30 Mar 2026 10:10:44 +0900 Subject: [PATCH] feat: show change timestamps in history compare --- backend/app/main.py | 6 +++++- legacy/static/organization.css | 4 ++++ legacy/static/organization.js | 21 ++++++++++++++++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/backend/app/main.py b/backend/app/main.py index eafd170..0dec3b9 100755 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -639,7 +639,8 @@ def fetch_members_as_of(cur, as_of: datetime) -> list[dict[str, object]]: mv.photo_url, COALESCE(m.sort_order, 2147483647) AS sort_order, mv.created_at, - mv.valid_from AS updated_at + mv.valid_from AS updated_at, + mv.valid_to AS history_valid_to FROM member_versions mv LEFT JOIN members m ON m.id = mv.member_id @@ -707,6 +708,7 @@ def build_member_compare_items(from_items: list[dict[str, object]], to_items: li "status": "added", "status_label": "신규", "categories": ["신규"], + "changed_at": after.get("updated_at"), "changes": [], "before_lines": [], "after_lines": build_summary(after), @@ -721,6 +723,7 @@ def build_member_compare_items(from_items: list[dict[str, object]], to_items: li "status": "removed", "status_label": "삭제", "categories": ["삭제"], + "changed_at": before.get("history_valid_to") or before.get("updated_at"), "changes": [], "before_lines": build_summary(before), "after_lines": [], @@ -757,6 +760,7 @@ def build_member_compare_items(from_items: list[dict[str, object]], to_items: li "status": "updated", "status_label": "변경", "categories": sorted(categories), + "changed_at": after.get("updated_at") or before.get("updated_at"), "changes": changes, "before_lines": [f"{change['label']}: {change['before'] or '-'}" for change in changes], "after_lines": [f"{change['label']}: {change['after'] or '-'}" for change in changes], diff --git a/legacy/static/organization.css b/legacy/static/organization.css index c88e349..6c26c97 100644 --- a/legacy/static/organization.css +++ b/legacy/static/organization.css @@ -862,6 +862,10 @@ body { width: 82px; } +.col-compare-date { + width: 132px; +} + .col-compare-category { width: 120px; } diff --git a/legacy/static/organization.js b/legacy/static/organization.js index 779581c..2cafb9b 100644 --- a/legacy/static/organization.js +++ b/legacy/static/organization.js @@ -1479,6 +1479,23 @@ function getListSearchEntries() { })); } +function formatCompareChangedAt(value) { + const raw = String(value || '').trim(); + if (!raw) { + return '-'; + } + const date = new Date(raw); + if (Number.isNaN(date.getTime())) { + return raw; + } + const year = date.getFullYear(); + const month = pad(date.getMonth() + 1); + const day = pad(date.getDate()); + const hours = pad(date.getHours()); + const minutes = pad(date.getMinutes()); + return `${year}-${month}-${day} ${hours}:${minutes}`; +} + function renderListViewCompareTable() { const container = document.getElementById('list-table-container'); if (!container) { @@ -1492,6 +1509,7 @@ function renderListViewCompareTable() { 이름 상태 + 변경일시 변경유형 이전 현재 @@ -1501,7 +1519,7 @@ function renderListViewCompareTable() { `; if (!rows.length) { - html += '선택한 기간 사이의 구성원 변경 내역이 없습니다.'; + html += '선택한 기간 사이의 구성원 변경 내역이 없습니다.'; } else { rows.forEach((item) => { const categories = (item.categories || []).map((category) => `${escapeHtml(category)}`).join(''); @@ -1511,6 +1529,7 @@ function renderListViewCompareTable() { ${escapeHtml(item.name || '-')} ${escapeHtml(item.status_label || '-')} + ${escapeHtml(formatCompareChangedAt(item.changed_at))}
${categories || '-'}
${beforeLines} ${afterLines}