1
0
forked from baron/baron-sso

RP 활성화/비활성화 UX 개선

This commit is contained in:
2026-02-04 10:38:41 +09:00
parent 1f1e7b6ce7
commit 1a02c15e78

View File

@@ -164,6 +164,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
Future<List<LinkedRp>>? _linkedRpsFuture; Future<List<LinkedRp>>? _linkedRpsFuture;
bool _showAllActivities = false; bool _showAllActivities = false;
final Set<String> _revokedClientIds = {};
@override @override
void initState() { void initState() {
@@ -213,7 +214,9 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('$appName 연동이 해지되었습니다.')), SnackBar(content: Text('$appName 연동이 해지되었습니다.')),
); );
_refreshAll(); setState(() {
_revokedClientIds.add(clientId);
});
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
@@ -297,6 +300,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
await ref.read(profileProvider.notifier).loadProfile(); await ref.read(profileProvider.notifier).loadProfile();
await _loadAuditLogs(reset: true); await _loadAuditLogs(reset: true);
setState(() { setState(() {
_revokedClientIds.clear();
_linkedRpsFuture = _fetchLinkedRps(); _linkedRpsFuture = _fetchLinkedRps();
}); });
if (_linkedRpsFuture != null) { if (_linkedRpsFuture != null) {
@@ -866,11 +870,13 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
List<_ActivityItem> _buildActivityItems(List<LinkedRp> linkedRps) { List<_ActivityItem> _buildActivityItems(List<LinkedRp> linkedRps) {
final items = <_ActivityItem>[]; final items = <_ActivityItem>[];
for (final rp in linkedRps) { for (final rp in linkedRps) {
final isRevoked = _revokedClientIds.contains(rp.id);
final lastAuthLabel = rp.lastAuthenticatedAt != null final lastAuthLabel = rp.lastAuthenticatedAt != null
? _formatDateTime(rp.lastAuthenticatedAt!) ? _formatDateTime(rp.lastAuthenticatedAt!)
: '연동됨'; : '연동됨';
final normalizedStatus = rp.status.toLowerCase(); final normalizedStatus = rp.status.toLowerCase();
final statusLabel = normalizedStatus.isEmpty || normalizedStatus == 'active' ? '활성' : '비활성'; final statusLabel = isRevoked ? '비활성' : (normalizedStatus.isEmpty || normalizedStatus == 'active' ? '활성' : '비활성');
final name = rp.name.isNotEmpty ? rp.name : rp.id; final name = rp.name.isNotEmpty ? rp.name : rp.id;
items.add( items.add(
_ActivityItem( _ActivityItem(
@@ -879,7 +885,8 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
lastAuthAt: lastAuthLabel, lastAuthAt: lastAuthLabel,
status: statusLabel, status: statusLabel,
canLogout: false, canLogout: false,
onRevoke: () => _onRevokeLink(rp.id, name), isRevoked: isRevoked,
onRevoke: isRevoked ? null : () => _onRevokeLink(rp.id, name),
), ),
); );
} }
@@ -933,14 +940,27 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
} }
Widget _buildActivityCard(_ActivityItem item) { Widget _buildActivityCard(_ActivityItem item) {
final statusColor = item.status == '활성' ? Colors.green : Colors.grey; final isActive = item.status == '활성';
return Container( final statusColor = isActive ? Colors.green : Colors.grey;
final borderColor = isActive ? Colors.green.withOpacity(0.5) : _border;
final borderWidth = isActive ? 1.5 : 1.0;
return Opacity(
opacity: item.isRevoked ? 0.6 : 1.0,
child: Container(
width: 260, width: 260,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _surface, color: _surface,
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(14),
border: Border.all(color: _border), border: Border.all(color: borderColor, width: borderWidth),
boxShadow: isActive ? [
BoxShadow(
color: Colors.green.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
)
] : null,
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -994,25 +1014,26 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
if (item.canLogout) const SizedBox(width: 8), if (item.canLogout) const SizedBox(width: 8),
Expanded( Expanded(
child: OutlinedButton( child: OutlinedButton(
onPressed: _isRevoking ? null : item.onRevoke, onPressed: (_isRevoking || item.isRevoked) ? null : item.onRevoke,
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
foregroundColor: Colors.redAccent, foregroundColor: item.isRevoked ? Colors.grey : Colors.redAccent,
side: const BorderSide(color: Colors.redAccent, width: 0.5), side: BorderSide(color: item.isRevoked ? Colors.grey : Colors.redAccent, width: 0.5),
padding: const EdgeInsets.symmetric(vertical: 8), padding: const EdgeInsets.symmetric(vertical: 8),
), ),
child: _isRevoking child: _isRevoking && !item.isRevoked
? const SizedBox( ? const SizedBox(
width: 14, width: 14,
height: 14, height: 14,
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.redAccent), child: CircularProgressIndicator(strokeWidth: 2, color: Colors.redAccent),
) )
: const Text('연동 해지', style: TextStyle(fontSize: 13)), : Text(item.isRevoked ? '해지됨' : '연동 해지', style: const TextStyle(fontSize: 13)),
), ),
), ),
], ],
), ),
], ],
), ),
),
); );
} }
@@ -1212,6 +1233,7 @@ class _ActivityItem {
final String lastAuthAt; final String lastAuthAt;
final String status; final String status;
final bool canLogout; final bool canLogout;
final bool isRevoked;
final VoidCallback? onLogout; final VoidCallback? onLogout;
final VoidCallback? onRevoke; final VoidCallback? onRevoke;
@@ -1221,6 +1243,7 @@ class _ActivityItem {
required this.lastAuthAt, required this.lastAuthAt,
required this.status, required this.status,
required this.canLogout, required this.canLogout,
this.isRevoked = false,
this.onLogout, this.onLogout,
this.onRevoke, this.onRevoke,
}); });