Skip to content

Commit 961c25d

Browse files
author
Lasim
committed
feat: enhance user detail view with internationalization support and improved layout
1 parent 7449649 commit 961c25d

File tree

2 files changed

+107
-82
lines changed

2 files changed

+107
-82
lines changed

services/frontend/src/i18n/locales/en/adminUsers.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,35 @@ export default {
2929
previous: 'Previous',
3030
next: 'Next',
3131
rowsSelected: '{selected} of {total} row(s) selected.'
32+
},
33+
userDetail: {
34+
title: 'User: {username}',
35+
titleLoading: 'User: Loading...',
36+
backToUsers: 'Back to Users',
37+
loading: 'Loading user details...',
38+
errorLoading: 'Error loading user: {error}',
39+
userInformation: 'User Information',
40+
personalDetails: 'Personal details and account information.',
41+
fields: {
42+
fullName: 'Full name',
43+
username: 'Username',
44+
emailAddress: 'Email address',
45+
role: 'Role',
46+
registrationMethod: 'Registration method',
47+
githubId: 'GitHub ID',
48+
accountDetails: 'Account details',
49+
permissions: 'Permissions'
50+
},
51+
values: {
52+
notProvided: 'Not provided',
53+
noRoleAssigned: 'No role assigned',
54+
email: 'Email',
55+
github: 'GitHub',
56+
firstName: 'First name:',
57+
lastName: 'Last name:',
58+
userId: 'User ID:',
59+
active: 'Active'
60+
}
3261
}
3362
},
3463
}

services/frontend/src/views/admin/UserDetail.vue

Lines changed: 78 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,8 @@ import { ref, onMounted, computed } from 'vue'
33
import { useRoute, useRouter } from 'vue-router'
44
import { useI18n } from 'vue-i18n'
55
import { Button } from '@/components/ui/button'
6-
import {
7-
Card,
8-
CardContent,
9-
CardDescription,
10-
CardHeader,
11-
CardTitle,
12-
} from '@/components/ui/card'
13-
import { Label } from '@/components/ui/label'
146
import { Badge } from '@/components/ui/badge'
15-
import { ArrowLeft, Mail, Github } from 'lucide-vue-next'
7+
import { ArrowLeft, Mail, Github, Shield } from 'lucide-vue-next'
168
import DashboardLayout from '@/components/DashboardLayout.vue'
179
import { getEnv } from '@/utils/env'
1810
import type { User } from './users/types'
@@ -75,7 +67,7 @@ const authTypeBadge = computed(() => {
7567
return {
7668
variant: (isEmail ? 'default' : 'secondary') as 'default' | 'secondary',
7769
icon: isEmail ? Mail : Github,
78-
text: isEmail ? 'Email' : 'GitHub'
70+
text: isEmail ? t('adminUsers.userDetail.values.email') : t('adminUsers.userDetail.values.github')
7971
}
8072
})
8173
@@ -85,7 +77,7 @@ const goBack = () => {
8577
</script>
8678

8779
<template>
88-
<DashboardLayout :title="`User: ${user?.username || 'Loading...'}`">
80+
<DashboardLayout :title="user ? t('adminUsers.userDetail.title', { username: user.username }) : t('adminUsers.userDetail.titleLoading')">
8981
<div class="space-y-6">
9082
<!-- Back Button -->
9183
<div>
@@ -95,112 +87,116 @@ const goBack = () => {
9587
class="mb-4"
9688
>
9789
<ArrowLeft class="h-4 w-4 mr-2" />
98-
Back to Users
90+
{{ t('adminUsers.userDetail.backToUsers') }}
9991
</Button>
10092
</div>
10193

10294
<!-- Loading State -->
10395
<div v-if="isLoading" class="text-muted-foreground">
104-
Loading user details...
96+
{{ t('adminUsers.userDetail.loading') }}
10597
</div>
10698

10799
<!-- Error State -->
108100
<div v-else-if="error" class="text-red-500">
109-
Error loading user: {{ error }}
101+
{{ t('adminUsers.userDetail.errorLoading', { error }) }}
110102
</div>
111103

112104
<!-- User Details -->
113-
<div v-else-if="user" class="space-y-6">
114-
<Card>
115-
<CardHeader>
116-
<CardTitle>Profile</CardTitle>
117-
<CardDescription>
118-
User information and account details.
119-
</CardDescription>
120-
</CardHeader>
121-
<CardContent class="space-y-6">
122-
<!-- Username -->
123-
<div class="space-y-2">
124-
<Label>Username</Label>
125-
<div class="text-sm font-medium">{{ user.username }}</div>
126-
</div>
127-
128-
<!-- Email -->
129-
<div class="space-y-2">
130-
<Label>Email</Label>
131-
<div class="text-sm">{{ user.email }}</div>
105+
<div v-else-if="user">
106+
<div class="px-4 sm:px-0">
107+
<h3 class="text-base/7 font-semibold text-gray-900">{{ t('adminUsers.userDetail.userInformation') }}</h3>
108+
<p class="mt-1 max-w-2xl text-sm/6 text-gray-500">{{ t('adminUsers.userDetail.personalDetails') }}</p>
109+
</div>
110+
<div class="mt-6 border-t border-gray-100">
111+
<dl class="divide-y divide-gray-100">
112+
<!-- Full Name -->
113+
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
114+
<dt class="text-sm/6 font-medium text-gray-900">{{ t('adminUsers.userDetail.fields.fullName') }}</dt>
115+
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">
116+
{{ displayName === user.username ? t('adminUsers.userDetail.values.notProvided') : displayName }}
117+
</dd>
132118
</div>
133119

134-
<!-- Full Name -->
135-
<div class="space-y-2">
136-
<Label>Full Name</Label>
137-
<div class="text-sm">
138-
{{ displayName === user.username ? 'Not provided' : displayName }}
139-
</div>
120+
<!-- Username -->
121+
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
122+
<dt class="text-sm/6 font-medium text-gray-900">{{ t('adminUsers.userDetail.fields.username') }}</dt>
123+
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">{{ user.username }}</dd>
140124
</div>
141125

142-
<!-- First Name -->
143-
<div class="space-y-2">
144-
<Label>First Name</Label>
145-
<div class="text-sm">{{ user.first_name || 'Not provided' }}</div>
126+
<!-- Email -->
127+
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
128+
<dt class="text-sm/6 font-medium text-gray-900">{{ t('adminUsers.userDetail.fields.emailAddress') }}</dt>
129+
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">{{ user.email }}</dd>
146130
</div>
147131

148-
<!-- Last Name -->
149-
<div class="space-y-2">
150-
<Label>Last Name</Label>
151-
<div class="text-sm">{{ user.last_name || 'Not provided' }}</div>
132+
<!-- Role -->
133+
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
134+
<dt class="text-sm/6 font-medium text-gray-900">{{ t('adminUsers.userDetail.fields.role') }}</dt>
135+
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">
136+
{{ user.role ? user.role.name : t('adminUsers.userDetail.values.noRoleAssigned') }}
137+
</dd>
152138
</div>
153139

154-
<!-- Authentication Type -->
155-
<div class="space-y-2">
156-
<Label>Registration Method</Label>
157-
<div v-if="authTypeBadge">
140+
<!-- Registration Method -->
141+
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
142+
<dt class="text-sm/6 font-medium text-gray-900">{{ t('adminUsers.userDetail.fields.registrationMethod') }}</dt>
143+
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">
158144
<Badge
145+
v-if="authTypeBadge"
159146
:variant="authTypeBadge.variant"
160147
class="flex items-center gap-1 w-fit"
161148
>
162149
<component :is="authTypeBadge.icon" class="h-3 w-3" />
163150
{{ authTypeBadge.text }}
164151
</Badge>
165-
</div>
152+
</dd>
166153
</div>
167154

168155
<!-- GitHub ID (if applicable) -->
169-
<div v-if="user.github_id" class="space-y-2">
170-
<Label>GitHub ID</Label>
171-
<div class="text-sm font-mono">{{ user.github_id }}</div>
172-
</div>
173-
174-
<!-- Role -->
175-
<div class="space-y-2">
176-
<Label>Role</Label>
177-
<div class="text-sm">
178-
{{ user.role ? user.role.name : 'No role assigned' }}
179-
</div>
156+
<div v-if="user.github_id" class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
157+
<dt class="text-sm/6 font-medium text-gray-900">{{ t('adminUsers.userDetail.fields.githubId') }}</dt>
158+
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">
159+
<span class="font-mono">{{ user.github_id }}</span>
160+
</dd>
180161
</div>
181162

182-
<!-- Role Permissions (if role exists) -->
183-
<div v-if="user.role && user.role.permissions.length > 0" class="space-y-2">
184-
<Label>Permissions</Label>
185-
<div class="flex flex-wrap gap-1">
186-
<Badge
187-
v-for="permission in user.role.permissions"
188-
:key="permission"
189-
variant="outline"
190-
class="text-xs"
191-
>
192-
{{ permission }}
193-
</Badge>
194-
</div>
163+
<!-- User Details -->
164+
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
165+
<dt class="text-sm/6 font-medium text-gray-900">{{ t('adminUsers.userDetail.fields.accountDetails') }}</dt>
166+
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">
167+
<div class="space-y-2">
168+
<div><span class="font-medium">{{ t('adminUsers.userDetail.values.firstName') }}</span> {{ user.first_name || t('adminUsers.userDetail.values.notProvided') }}</div>
169+
<div><span class="font-medium">{{ t('adminUsers.userDetail.values.lastName') }}</span> {{ user.last_name || t('adminUsers.userDetail.values.notProvided') }}</div>
170+
<div><span class="font-medium">{{ t('adminUsers.userDetail.values.userId') }}</span> <span class="font-mono text-xs">{{ user.id }}</span></div>
171+
</div>
172+
</dd>
195173
</div>
196174

197-
<!-- User ID -->
198-
<div class="space-y-2">
199-
<Label>User ID</Label>
200-
<div class="text-sm font-mono text-muted-foreground">{{ user.id }}</div>
175+
<!-- Permissions (if role exists) -->
176+
<div v-if="user.role && user.role.permissions.length > 0" class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
177+
<dt class="text-sm/6 font-medium text-gray-900">{{ t('adminUsers.userDetail.fields.permissions') }}</dt>
178+
<dd class="mt-2 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
179+
<ul role="list" class="divide-y divide-gray-100 rounded-md border border-gray-200">
180+
<li
181+
v-for="permission in user.role.permissions"
182+
:key="permission"
183+
class="flex items-center justify-between py-4 pr-5 pl-4 text-sm/6"
184+
>
185+
<div class="flex w-0 flex-1 items-center">
186+
<Shield class="size-5 shrink-0 text-gray-400" aria-hidden="true" />
187+
<div class="ml-4 flex min-w-0 flex-1 gap-2">
188+
<span class="truncate font-medium">{{ permission }}</span>
189+
</div>
190+
</div>
191+
<div class="ml-4 shrink-0">
192+
<Badge variant="outline" class="text-xs">{{ t('adminUsers.userDetail.values.active') }}</Badge>
193+
</div>
194+
</li>
195+
</ul>
196+
</dd>
201197
</div>
202-
</CardContent>
203-
</Card>
198+
</dl>
199+
</div>
204200
</div>
205201
</div>
206202
</DashboardLayout>

0 commit comments

Comments
 (0)