From c95105f01878cb43bbcc5be6399ac062f9cb1a32 Mon Sep 17 00:00:00 2001 From: kyy Date: Tue, 7 Apr 2026 13:38:13 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A0=91=EC=86=8D=EC=9D=B4=EB=A0=A5=20?= =?UTF-8?q?=EB=B8=8C=EB=9D=BC=EC=9A=B0=EC=A0=80=20=EC=BB=AC=EB=9F=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en.toml | 4 +- locales/ko.toml | 4 +- userfront/assets/translations/en.toml | 4 +- userfront/assets/translations/ko.toml | 4 +- userfront/assets/translations/template.toml | 4 +- .../presentation/dashboard_screen.dart | 68 +++++++++++++++---- userfront/lib/i18n_data.dart | 4 ++ 7 files changed, 72 insertions(+), 20 deletions(-) diff --git a/locales/en.toml b/locales/en.toml index aa9be933..cbc9ab29 100644 --- a/locales/en.toml +++ b/locales/en.toml @@ -509,6 +509,7 @@ saved_success = "Saved successfully." greeting = "Hello, {{name}}." [msg.userfront.audit] +browser = "Browser: {{value}}" date = "Date: {{value}}" device = "Device: {{value}}" end = "No more items to show." @@ -2058,6 +2059,7 @@ dev_console = "Dev Console" action = "Action" app = "App" auth_method = "Auth Method" +browser = "Browser" date = "Date" device = "Device" ip = "IP" @@ -2286,4 +2288,4 @@ title = "Manage My Activity" toggle_label = "Show active sessions only" [msg.userfront.audit.filter] -description = "Toggle to view only active sessions." \ No newline at end of file +description = "Toggle to view only active sessions." diff --git a/locales/ko.toml b/locales/ko.toml index 0c4c27a1..6243c0af 100644 --- a/locales/ko.toml +++ b/locales/ko.toml @@ -171,6 +171,7 @@ missing_jwks_uri = "JWKS URI를 입력해야 합니다." private_key_jwt_requires_public_key = "서명 키 기반 인증을 사용하려면 JWKS URI가 필요합니다." [msg.userfront.audit] +browser = "브라우저: {{value}}" date = "접속일자: {{value}}" device = "접속환경: {{value}}" end = "더 이상 항목이 없습니다." @@ -2452,6 +2453,7 @@ dev_console = "Dev Console" action = "관리" app = "애플리케이션" auth_method = "인증수단" +browser = "브라우저" date = "접속일자" device = "접속환경" ip = "IP" @@ -2679,4 +2681,4 @@ title = "내 활동 관리" toggle_label = "활성 세션만 보기" [msg.userfront.audit.filter] -description = "활성화된 세션만 보려면 토글을 켜주세요." \ No newline at end of file +description = "활성화된 세션만 보려면 토글을 켜주세요." diff --git a/userfront/assets/translations/en.toml b/userfront/assets/translations/en.toml index 33df87a6..5f5a3298 100644 --- a/userfront/assets/translations/en.toml +++ b/userfront/assets/translations/en.toml @@ -44,6 +44,7 @@ missing = "No active session was found." greeting = "Hello, {name}." [msg.userfront.audit] +browser = "Browser: {value}" date = "Date: {value}" device = "Device: {value}" end = "No more items to show." @@ -444,6 +445,7 @@ toggle_label = "Active only" action = "Action" app = "App" auth_method = "Auth Method" +browser = "Browser" date = "Date" device = "Device" ip = "IP" @@ -668,4 +670,4 @@ action = "Go to sign-in" [msg.userfront.audit.filter] -description = "Toggle to view only active sessions." \ No newline at end of file +description = "Toggle to view only active sessions." diff --git a/userfront/assets/translations/ko.toml b/userfront/assets/translations/ko.toml index da621486..eee97bbe 100644 --- a/userfront/assets/translations/ko.toml +++ b/userfront/assets/translations/ko.toml @@ -41,6 +41,7 @@ verify_code_failed = "인증 실패: {error}" missing = "활성 세션이 없습니다." [msg.userfront.audit] +browser = "브라우저: {value}" date = "접속일자: {value}" device = "접속환경: {value}" end = "더 이상 항목이 없습니다." @@ -647,6 +648,7 @@ toggle_label = "활성 세션만" action = "관리" app = "애플리케이션" auth_method = "인증수단" +browser = "브라우저" date = "접속일자" device = "접속환경" ip = "IP" @@ -870,4 +872,4 @@ action = "로그인하기" [msg.userfront.audit.filter] -description = "활성화된 세션만 보려면 토글을 켜주세요." \ No newline at end of file +description = "활성화된 세션만 보려면 토글을 켜주세요." diff --git a/userfront/assets/translations/template.toml b/userfront/assets/translations/template.toml index d5c036db..f3d544ba 100644 --- a/userfront/assets/translations/template.toml +++ b/userfront/assets/translations/template.toml @@ -223,6 +223,7 @@ title = "" greeting = "" [msg.userfront.audit] +browser = "" date = "" device = "" end = "" @@ -622,6 +623,7 @@ toggle_label = "" action = "" app = "" auth_method = "" +browser = "" date = "" device = "" ip = "" @@ -845,4 +847,4 @@ action = "" [msg.userfront.audit.filter] -description = "" \ No newline at end of file +description = "" diff --git a/userfront/lib/features/dashboard/presentation/dashboard_screen.dart b/userfront/lib/features/dashboard/presentation/dashboard_screen.dart index 8b575324..a7ba6f24 100644 --- a/userfront/lib/features/dashboard/presentation/dashboard_screen.dart +++ b/userfront/lib/features/dashboard/presentation/dashboard_screen.dart @@ -40,6 +40,8 @@ class _DashboardScreenState extends ConsumerState { static const double _historySessionMinWidth = 92; static const double _historyOtherColumnsBaselineWidth = 780; static const int _historySessionMinVisibleChars = 8; + static const double _historyStatusColumnWidth = 92; + static const double _historyActionColumnWidth = 108; final ScrollController _pageScrollController = ScrollController(); final ScrollController _rpScrollController = ScrollController(); @@ -1678,18 +1680,23 @@ class _DashboardScreenState extends ConsumerState { } Widget _buildHistoryStatusBadge(_HistorySessionStatus status) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - color: _historySessionStatusColor(status), - borderRadius: BorderRadius.circular(999), - ), - child: Text( - _historySessionStatusLabel(status), - style: const TextStyle( - fontSize: 11, - color: Colors.white, - fontWeight: FontWeight.w600, + return SizedBox( + width: _historyStatusColumnWidth, + child: Center( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: _historySessionStatusColor(status), + borderRadius: BorderRadius.circular(999), + ), + child: Text( + _historySessionStatusLabel(status), + style: const TextStyle( + fontSize: 11, + color: Colors.white, + fontWeight: FontWeight.w600, + ), + ), ), ), ); @@ -1703,7 +1710,7 @@ class _DashboardScreenState extends ConsumerState { final canRevoke = !isCurrent && _revokingSessionId == null && session.isActive; return SizedBox( - width: 108, + width: _historyActionColumnWidth, child: OutlinedButton( onPressed: canRevoke ? () => _onRevokeSession(session) : null, style: OutlinedButton.styleFrom( @@ -1797,6 +1804,9 @@ class _DashboardScreenState extends ConsumerState { DataColumn( label: Text(tr('ui.userfront.audit.table.device')), ), + DataColumn( + label: Text(tr('ui.userfront.audit.table.browser')), + ), DataColumn( label: Text(tr('ui.userfront.audit.table.auth_method')), ), @@ -1804,10 +1814,20 @@ class _DashboardScreenState extends ConsumerState { label: Text(tr('ui.userfront.audit.table.result')), ), DataColumn( - label: Text(tr('ui.userfront.audit.table.status')), + label: SizedBox( + width: _historyStatusColumnWidth, + child: Center( + child: Text(tr('ui.userfront.audit.table.status')), + ), + ), ), DataColumn( - label: Text(tr('ui.userfront.audit.table.action')), + label: SizedBox( + width: _historyActionColumnWidth, + child: Center( + child: Text(tr('ui.userfront.audit.table.action')), + ), + ), ), ], rows: items.map((log) { @@ -1828,6 +1848,7 @@ class _DashboardScreenState extends ConsumerState { final deviceLabel = _deviceLabelFromUserAgent( log.userAgent, ); + final browserLabel = _sessionBrowserLabel(log.userAgent); return DataRow( cells: [ DataCell( @@ -1853,6 +1874,13 @@ class _DashboardScreenState extends ConsumerState { ), ), DataCell(_selectableText(deviceLabel)), + DataCell( + _selectableText( + browserLabel.isEmpty + ? tr('ui.common.hyphen', fallback: '-') + : browserLabel, + ), + ), DataCell(_buildAuthMethodCell(log, authMethod)), DataCell( _selectableText( @@ -2005,6 +2033,16 @@ class _DashboardScreenState extends ConsumerState { }, ), ), + _selectableText( + tr( + 'msg.userfront.audit.browser', + params: { + 'value': _sessionBrowserLabel(log.userAgent).isEmpty + ? tr('ui.common.hyphen', fallback: '-') + : _sessionBrowserLabel(log.userAgent), + }, + ), + ), _buildAuthMethodLine( log, log.authMethod.isNotEmpty diff --git a/userfront/lib/i18n_data.dart b/userfront/lib/i18n_data.dart index b1a416c3..7974493c 100644 --- a/userfront/lib/i18n_data.dart +++ b/userfront/lib/i18n_data.dart @@ -413,6 +413,7 @@ const Map koStrings = { "msg.dev.sidebar.notice": "개발자 전용 콘솔입니다.", "msg.dev.sidebar.notice_detail": "연동 앱 등록 및 관리를 수행할 수 있습니다.", "msg.info.saved_success": "저장이 완료되었습니다.", + "msg.userfront.audit.browser": "브라우저: {{value}}", "msg.userfront.audit.date": "접속일자: {{value}}", "msg.userfront.audit.device": "접속환경: {{value}}", "msg.userfront.audit.end": "더 이상 항목이 없습니다.", @@ -1697,6 +1698,7 @@ const Map koStrings = { "ui.userfront.audit.table.action": "관리", "ui.userfront.audit.table.app": "애플리케이션", "ui.userfront.audit.table.auth_method": "인증수단", + "ui.userfront.audit.table.browser": "브라우저", "ui.userfront.audit.table.date": "접속일자", "ui.userfront.audit.table.device": "접속환경", "ui.userfront.audit.table.ip": "IP", @@ -2316,6 +2318,7 @@ const Map enStrings = { "msg.dev.sidebar.notice": "Developer Console", "msg.dev.sidebar.notice_detail": "Register and manage client applications.", "msg.info.saved_success": "Saved successfully.", + "msg.userfront.audit.browser": "Browser: {{value}}", "msg.userfront.audit.date": "Date: {{value}}", "msg.userfront.audit.device": "Device: {{value}}", "msg.userfront.audit.end": "No more items to show.", @@ -3705,6 +3708,7 @@ const Map enStrings = { "ui.userfront.audit.table.action": "Action", "ui.userfront.audit.table.app": "App", "ui.userfront.audit.table.auth_method": "Auth Method", + "ui.userfront.audit.table.browser": "Browser", "ui.userfront.audit.table.date": "Date", "ui.userfront.audit.table.device": "Device", "ui.userfront.audit.table.ip": "IP",