Skip to content

Commit bfd2bbc

Browse files
author
Lasim
committed
feat(frontend): enhance button components with loading states and text
1 parent 7fe7443 commit bfd2bbc

File tree

7 files changed

+89
-45
lines changed

7 files changed

+89
-45
lines changed

services/frontend/src/components/teams/AddTeamModal.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,11 @@ const handleDescriptionChange = () => {
198198
</Button>
199199
<Button
200200
type="submit"
201-
:disabled="!isFormValid || isSubmitting"
201+
:disabled="!isFormValid"
202+
:loading="isSubmitting"
203+
:loading-text="t('teams.addModal.buttons.creating')"
202204
>
203-
{{ isSubmitting ? t('teams.addModal.buttons.creating') : t('teams.addModal.buttons.create') }}
205+
{{ t('teams.addModal.buttons.create') }}
204206
</Button>
205207
</AlertDialogFooter>
206208
</form>

services/frontend/src/components/teams/manage/TeamDangerZone.vue

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,14 @@ import { useRouter } from 'vue-router'
55
import { toast } from 'vue-sonner'
66
import { Button } from '@/components/ui/button'
77
import { Alert, AlertDescription } from '@/components/ui/alert'
8-
import {
9-
Trash2,
8+
import {
9+
Trash2,
1010
AlertTriangle,
1111
XCircle,
12-
Loader2,
1312
Shield
1413
} from 'lucide-vue-next'
1514
import {
1615
AlertDialog,
17-
AlertDialogAction,
1816
AlertDialogCancel,
1917
AlertDialogContent,
2018
AlertDialogDescription,
@@ -45,7 +43,7 @@ const deleteTeam = async () => {
4543
isDeleting.value = true
4644
error.value = null
4745
const teamName = props.team.name || 'Unknown Team'
48-
46+
4947
await TeamService.deleteTeam(props.team.id)
5048
5149
// Show success toast
@@ -102,7 +100,7 @@ const deleteTeam = async () => {
102100
<p class="text-sm text-muted-foreground mb-4">
103101
Permanently delete this team and all associated resources. This action cannot be undone.
104102
</p>
105-
103+
106104
<!-- What gets deleted -->
107105
<div class="bg-background border rounded-lg p-4 mb-4">
108106
<h4 class="text-sm font-medium mb-3">This will permanently delete:</h4>
@@ -177,12 +175,12 @@ const deleteTeam = async () => {
177175
</AlertDialogTitle>
178176
<AlertDialogDescription class="space-y-4">
179177
<p>{{ t('teams.manage.deleteDialog.warning') }}</p>
180-
178+
181179
<div class="rounded-lg border p-3 bg-muted/50">
182180
<p class="font-medium text-sm mb-1">{{ t('teams.manage.deleteDialog.teamName') }}:</p>
183181
<p class="font-mono text-sm">"{{ team.name }}"</p>
184182
</div>
185-
183+
186184
<div class="rounded-lg border-destructive/50 bg-destructive/5 p-4 space-y-3">
187185
<p class="text-sm font-medium text-destructive">{{ t('teams.manage.deleteDialog.consequences') }}</p>
188186
<ul class="text-xs space-y-2">
@@ -210,15 +208,16 @@ const deleteTeam = async () => {
210208
<AlertDialogCancel @click="showDeleteDialog = false">
211209
{{ t('teams.manage.deleteDialog.cancel') }}
212210
</AlertDialogCancel>
213-
<AlertDialogAction
211+
<Button
212+
variant="destructive"
214213
@click="deleteTeam"
215-
:disabled="isDeleting"
214+
:loading="isDeleting"
215+
:loading-text="t('teams.manage.deleteDialog.deleting')"
216216
class="bg-destructive hover:bg-destructive/90 gap-2"
217217
>
218-
<Loader2 v-if="isDeleting" class="h-4 w-4 animate-spin" />
219-
<Trash2 v-else class="h-4 w-4" />
220-
{{ isDeleting ? t('teams.manage.deleteDialog.deleting') : t('teams.manage.deleteDialog.confirm') }}
221-
</AlertDialogAction>
218+
<Trash2 class="h-4 w-4" />
219+
{{ t('teams.manage.deleteDialog.confirm') }}
220+
</Button>
222221
</AlertDialogFooter>
223222
</AlertDialogContent>
224223
</AlertDialog>

services/frontend/src/components/teams/manage/TeamInfo.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
Calendar,
1212
Users,
1313
Hash,
14-
Loader2,
1514
AlertTriangle
1615
} from 'lucide-vue-next'
1716
import { TeamService, type Team } from '@/services/teamService'
@@ -249,12 +248,13 @@ watch(() => props.team, () => {
249248
<div class="flex items-center justify-end pt-4 border-t">
250249
<Button
251250
@click="saveTeam"
252-
:disabled="!hasChanges || isSaving"
251+
:disabled="!hasChanges"
252+
:loading="isSaving"
253+
:loading-text="t('teams.manage.saving')"
253254
class="gap-2"
254255
>
255-
<Loader2 v-if="isSaving" class="h-4 w-4 animate-spin" />
256-
<Save v-else class="h-4 w-4" />
257-
{{ isSaving ? t('teams.manage.saving') : t('teams.manage.save') }}
256+
<Save class="h-4 w-4" />
257+
{{ t('teams.manage.save') }}
258258
</Button>
259259
</div>
260260
</div>

services/frontend/src/components/teams/manage/members/AddMemberModal.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,12 @@ const handleCancel = () => {
224224
<Button type="button" variant="outline" @click="handleCancel" :disabled="isAddingMember">
225225
{{ t('teams.manage.members.addModal.buttons.cancel') }}
226226
</Button>
227-
<Button type="submit" :disabled="isAddingMember">
228-
{{ isAddingMember ? t('teams.manage.members.addModal.buttons.adding') : t('teams.manage.members.addModal.buttons.add') }}
227+
<Button
228+
type="submit"
229+
:loading="isAddingMember"
230+
:loading-text="t('teams.manage.members.addModal.buttons.adding')"
231+
>
232+
{{ t('teams.manage.members.addModal.buttons.add') }}
229233
</Button>
230234
</DialogFooter>
231235
</form>

services/frontend/src/components/teams/manage/members/EditRoleModal.vue

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ const roleOptions = [
5555
{
5656
value: 'team_admin' as const,
5757
label: t('teams.manage.members.roles.admin'),
58-
description: 'Can manage team members and all team resources'
58+
description: t('teams.manage.members.editRoleModal.roleDescriptions.admin')
5959
},
6060
{
6161
value: 'team_user' as const,
6262
label: t('teams.manage.members.roles.user'),
63-
description: 'Can view team resources with limited permissions'
63+
description: t('teams.manage.members.editRoleModal.roleDescriptions.user')
6464
}
6565
]
6666
@@ -89,7 +89,7 @@ const isRoleChanged = computed(() => {
8989
const getApiUrl = () => {
9090
const apiUrl = getEnv('VITE_DEPLOYSTACK_BACKEND_URL')
9191
if (!apiUrl) {
92-
throw new Error('API URL not configured. Make sure VITE_DEPLOYSTACK_BACKEND_URL is set.')
92+
throw new Error(t('teams.manage.members.editRoleModal.messages.apiUrlNotConfigured'))
9393
}
9494
return apiUrl
9595
}
@@ -137,28 +137,31 @@ const updateMemberRole = async () => {
137137
138138
if (!response.ok) {
139139
const errorData = await response.json().catch(() => ({}))
140-
throw new Error(errorData.error || `Failed to update member role: ${response.status}`)
140+
throw new Error(errorData.error || t('teams.manage.members.editRoleModal.messages.updateRoleFailed', { status: response.status }))
141141
}
142142
143143
const data = await response.json()
144144
145145
if (data.success) {
146146
// Show success toast
147147
const roleLabel = roleOptions.find(r => r.value === selectedRole.value)?.label || selectedRole.value
148-
toast.success('Role updated successfully!', {
149-
description: `${props.member.name} is now a ${roleLabel}`
148+
toast.success(t('teams.manage.members.editRoleModal.messages.success'), {
149+
description: t('teams.manage.members.editRoleModal.messages.successDescription', {
150+
memberName: props.member.name,
151+
role: roleLabel
152+
})
150153
})
151154
152155
// Close modal and emit success
153156
emit('update:open', false)
154157
emit('role-updated')
155158
} else {
156-
throw new Error(data.error || 'Unknown error occurred')
159+
throw new Error(data.error || t('teams.manage.members.editRoleModal.messages.unknownError'))
157160
}
158161
} catch (error) {
159162
console.error('Error updating member role:', error)
160-
toast.error('Failed to update role', {
161-
description: error instanceof Error ? error.message : 'Unknown error occurred'
163+
toast.error(t('teams.manage.members.editRoleModal.messages.error'), {
164+
description: error instanceof Error ? error.message : t('teams.manage.members.editRoleModal.messages.unknownError')
162165
})
163166
} finally {
164167
isUpdatingRole.value = false
@@ -174,9 +177,9 @@ const handleCancel = () => {
174177
<AlertDialog :open="props.open" @update:open="handleModalChange">
175178
<AlertDialogContent>
176179
<AlertDialogHeader>
177-
<AlertDialogTitle>Edit Team Member Role</AlertDialogTitle>
180+
<AlertDialogTitle>{{ $t('teams.manage.members.editRoleModal.title') }}</AlertDialogTitle>
178181
<AlertDialogDescription>
179-
Change the role and permissions for this team member.
182+
{{ $t('teams.manage.members.editRoleModal.description') }}
180183
</AlertDialogDescription>
181184
</AlertDialogHeader>
182185

@@ -197,29 +200,28 @@ const handleCancel = () => {
197200
<!-- Owner Notice -->
198201
<div v-if="isOwner" class="bg-blue-50 border border-blue-200 rounded-lg p-4">
199202
<p class="text-sm text-blue-800">
200-
<strong>Team Owner:</strong> The team owner's role cannot be changed.
201-
To change the owner's role, you must first transfer team ownership to another member.
203+
<strong>{{ $t('teams.manage.members.editRoleModal.ownerNotice.title') }}:</strong> {{ $t('teams.manage.members.editRoleModal.ownerNotice.description') }}
202204
</p>
203205
</div>
204206

205207
<!-- Role Selection -->
206208
<div v-else class="space-y-4">
207209
<!-- Current Role Display -->
208210
<div class="space-y-2">
209-
<Label class="text-sm font-medium">Current Role</Label>
211+
<Label class="text-sm font-medium">{{ $t('teams.manage.members.editRoleModal.currentRole.label') }}</Label>
210212
<div class="p-3 bg-muted/30 rounded-md">
211213
<p class="text-sm">
212-
{{ roleOptions.find(r => r.value === currentRole)?.label || 'Unknown Role' }}
214+
{{ roleOptions.find(r => r.value === currentRole)?.label || $t('teams.manage.members.editRoleModal.currentRole.unknown') }}
213215
</p>
214216
</div>
215217
</div>
216218

217219
<!-- New Role Selection -->
218220
<div class="space-y-2">
219-
<Label for="role-select" class="text-sm font-medium">Change Role To</Label>
221+
<Label for="role-select" class="text-sm font-medium">{{ $t('teams.manage.members.editRoleModal.newRole.label') }}</Label>
220222
<Select v-model="selectedRole">
221223
<SelectTrigger id="role-select">
222-
<SelectValue placeholder="Select new role" />
224+
<SelectValue :placeholder="$t('teams.manage.members.editRoleModal.newRole.placeholder')" />
223225
</SelectTrigger>
224226
<SelectContent>
225227
<SelectItem
@@ -240,14 +242,16 @@ const handleCancel = () => {
240242

241243
<AlertDialogFooter>
242244
<AlertDialogCancel @click="handleCancel">
243-
Cancel
245+
{{ $t('teams.manage.members.editRoleModal.buttons.cancel') }}
244246
</AlertDialogCancel>
245247
<Button
246248
v-if="canChangeRole"
247249
@click="updateMemberRole"
248-
:disabled="isUpdatingRole || !isRoleChanged"
250+
:disabled="!isRoleChanged"
251+
:loading="isUpdatingRole"
252+
:loading-text="$t('teams.manage.members.editRoleModal.buttons.updating')"
249253
>
250-
{{ isUpdatingRole ? 'Updating...' : 'Update Role' }}
254+
{{ $t('teams.manage.members.editRoleModal.buttons.update') }}
251255
</Button>
252256
</AlertDialogFooter>
253257
</AlertDialogContent>

services/frontend/src/components/teams/manage/members/RemoveMemberModal.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,13 @@ const handleCancel = () => {
186186
{{ t('teams.manage.members.removeModal.buttons.cancel') }}
187187
</AlertDialogCancel>
188188
<Button
189+
variant="destructive"
189190
@click="removeMember"
190-
:disabled="isRemovingMember"
191+
:loading="isRemovingMember"
192+
:loading-text="t('teams.manage.members.removeModal.buttons.removing')"
191193
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
192194
>
193-
{{ isRemovingMember ? t('teams.manage.members.removeModal.buttons.removing') : t('teams.manage.members.removeModal.buttons.remove') }}
195+
{{ t('teams.manage.members.removeModal.buttons.remove') }}
194196
</Button>
195197
</AlertDialogFooter>
196198
</AlertDialogContent>

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,39 @@ export default {
174174
removeMemberFailed: 'Failed to remove member: {status}',
175175
unknownError: 'Unknown error occurred'
176176
}
177+
},
178+
editRoleModal: {
179+
title: 'Edit Team Member Role',
180+
description: 'Change the role and permissions for this team member.',
181+
ownerNotice: {
182+
title: 'Team Owner',
183+
description: 'The team owner\'s role cannot be changed. To change the owner\'s role, you must first transfer team ownership to another member.'
184+
},
185+
currentRole: {
186+
label: 'Current Role',
187+
unknown: 'Unknown Role'
188+
},
189+
newRole: {
190+
label: 'Change Role To',
191+
placeholder: 'Select new role'
192+
},
193+
roleDescriptions: {
194+
admin: 'Can manage team members and all team resources',
195+
user: 'Can view team resources with limited permissions'
196+
},
197+
buttons: {
198+
cancel: 'Cancel',
199+
update: 'Update Role',
200+
updating: 'Updating...'
201+
},
202+
messages: {
203+
success: 'Role updated successfully!',
204+
successDescription: '{memberName} is now a {role}',
205+
error: 'Failed to update role',
206+
apiUrlNotConfigured: 'API URL not configured. Make sure VITE_DEPLOYSTACK_BACKEND_URL is set.',
207+
updateRoleFailed: 'Failed to update member role: {status}',
208+
unknownError: 'Unknown error occurred'
209+
}
177210
}
178211
},
179212
fields: {

0 commit comments

Comments
 (0)