feat: Add missing i18n translations for page components (partial)

This commit introduces internationalization for several page components by identifying hardcoded strings, adding them to translation files, and updating the components to use translation keys.

Processed pages:
- fe/src/pages/AuthCallbackPage.vue: I internationalized an error message.
- fe/src/pages/ChoresPage.vue: I internationalized console error messages and an input placeholder.
- fe/src/pages/ErrorNotFound.vue: I found no missing translations.
- fe/src/pages/GroupDetailPage.vue: I internationalized various UI elements (ARIA labels, button text, fallback user display names) and console/error messages.
- fe/src/pages/GroupsPage.vue: I internationalized error messages and console logs.
- fe/src/pages/IndexPage.vue: I found no missing user-facing translations.
- fe/src/pages/ListDetailPage.vue: My analysis is complete, and I identified a console message and a fallback string for translation (implementation of changes for this page is pending).

For each processed page where changes were needed:
- I added new keys to `fe/src/i18n/en.json`.
- I added corresponding placeholder keys `"[TRANSLATE] Original Text"` to `fe/src/i18n/de.json`, `fe/src/i18n/es.json`, and `fe/src/i18n/fr.json`.
- I updated the Vue component to use the `t()` function with the new keys.

Further pages in `fe/src/pages/` are pending analysis and internationalization as per our original plan.
This commit is contained in:
google-labs-jules[bot] 2025-06-07 20:40:49 +00:00
parent 550fac1c0c
commit 198222c3ff
8 changed files with 156 additions and 42 deletions

View File

@ -73,7 +73,10 @@
"groupNameRequired": "DE: Group name is required",
"createFailed": "DE: Failed to create group. Please try again.",
"inviteCodeRequired": "DE: Invite code is required",
"joinFailed": "DE: Failed to join group. Please check the invite code and try again."
"joinFailed": "DE: Failed to join group. Please check the invite code and try again.",
"invalidDataFromServer": "[TRANSLATE] Invalid data received from server.",
"createFailedConsole": "[TRANSLATE] Error creating group:",
"joinFailedConsole": "[TRANSLATE] Error joining group:"
},
"notifications": {
"groupCreatedSuccess": "DE: Group '{groupName}' created successfully.",
@ -85,7 +88,8 @@
"authCallbackPage": {
"redirecting": "DE: Redirecting...",
"errors": {
"authenticationFailed": "DE: Authentication failed"
"authenticationFailed": "DE: Authentication failed",
"noTokenProvided": "[TRANSLATE] No token provided"
}
},
"choresPage": {
@ -165,7 +169,17 @@
"quickDueDateTomorrow": "DE: Tomorrow",
"quickDueDateNextWeek": "DE: Next Week",
"cancelButton": "DE: Cancel",
"saveButton": "DE: Save"
"saveButton": "DE: Save",
"intervalPlaceholder": "[TRANSLATE] e.g., 10"
},
"consoleErrors": {
"loadFailed": "[TRANSLATE] Failed to load all chores:",
"loadGroupsFailed": "[TRANSLATE] Failed to load groups",
"createAssignmentForNewChoreFailed": "[TRANSLATE] Failed to create assignment for new chore:",
"saveFailed": "[TRANSLATE] Failed to save chore:",
"deleteFailed": "[TRANSLATE] Failed to delete chore:",
"createAssignmentFailed": "[TRANSLATE] Failed to create assignment:",
"updateCompletionStatusFailed": "[TRANSLATE] Failed to update chore completion status:"
},
"deleteDialog": {
"title": "DE: Delete Chore",
@ -228,10 +242,14 @@
"title": "DE: Group Members",
"defaultRole": "DE: Member",
"removeButton": "DE: Remove",
"emptyState": "DE: No members found."
"emptyState": "DE: No members found.",
"closeMenuLabel": "[TRANSLATE] Close menu"
},
"invites": {
"title": "DE: Invite Members",
"description": "[TRANSLATE] Invite new members by generating a shareable code.",
"addMemberButtonLabel": "[TRANSLATE] Add member",
"closeInviteLabel": "[TRANSLATE] Close invite",
"regenerateButton": "DE: Regenerate Invite Code",
"generateButton": "DE: Generate Invite Code",
"activeCodeLabel": "DE: Current Active Invite Code:",
@ -242,6 +260,15 @@
"newDataInvalid": "DE: New invite code data is invalid."
}
},
"errors": {
"failedToFetchActiveInvite": "[TRANSLATE] Failed to fetch active invite code.",
"failedToFetchGroupDetails": "[TRANSLATE] Failed to fetch group details.",
"failedToLoadUpcomingChores": "[TRANSLATE] Error loading upcoming chores:",
"failedToLoadRecentExpenses": "[TRANSLATE] Error loading recent expenses:"
},
"console": {
"noActiveInvite": "[TRANSLATE] No active invite code found for this group."
},
"chores": {
"title": "DE: Group Chores",
"manageButton": "DE: Manage Chores",
@ -252,6 +279,8 @@
"title": "DE: Group Expenses",
"manageButton": "DE: Manage Expenses",
"emptyState": "DE: No expenses recorded. Click \"Manage Expenses\" to add some!",
"fallbackUserName": "[TRANSLATE] User ID: {userId}",
"activityByUserFallback": "[TRANSLATE] User {userId}",
"splitTypes": {
"equal": "DE: Equal",
"exactAmounts": "DE: Exact Amounts",

View File

@ -73,7 +73,10 @@
"groupNameRequired": "Group name is required",
"createFailed": "Failed to create group. Please try again.",
"inviteCodeRequired": "Invite code is required",
"joinFailed": "Failed to join group. Please check the invite code and try again."
"joinFailed": "Failed to join group. Please check the invite code and try again.",
"invalidDataFromServer": "Invalid data received from server.",
"createFailedConsole": "Error creating group:",
"joinFailedConsole": "Error joining group:"
},
"notifications": {
"groupCreatedSuccess": "Group '{groupName}' created successfully.",
@ -85,7 +88,8 @@
"authCallbackPage": {
"redirecting": "Redirecting...",
"errors": {
"authenticationFailed": "Authentication failed"
"authenticationFailed": "Authentication failed",
"noTokenProvided": "No token provided"
}
},
"choresPage": {
@ -125,7 +129,17 @@
"save": "Save Changes",
"create": "Create",
"editChore": "Edit Chore",
"createChore": "Create Chore"
"createChore": "Create Chore",
"intervalPlaceholder": "e.g., 10"
},
"consoleErrors": {
"loadFailed": "Failed to load all chores:",
"loadGroupsFailed": "Failed to load groups",
"createAssignmentForNewChoreFailed": "Failed to create assignment for new chore:",
"saveFailed": "Failed to save chore:",
"deleteFailed": "Failed to delete chore:",
"createAssignmentFailed": "Failed to create assignment:",
"updateCompletionStatusFailed": "Failed to update chore completion status:"
},
"deleteConfirm": {
"title": "Confirm Deletion",
@ -160,10 +174,14 @@
"title": "Group Members",
"defaultRole": "Member",
"removeButton": "Remove",
"emptyState": "No members found."
"emptyState": "No members found.",
"closeMenuLabel": "Close menu"
},
"invites": {
"title": "Invite Members",
"description": "Invite new members by generating a shareable code.",
"addMemberButtonLabel": "Add member",
"closeInviteLabel": "Close invite",
"regenerateButton": "Regenerate Invite Code",
"generateButton": "Generate Invite Code",
"activeCodeLabel": "Current Active Invite Code:",
@ -174,6 +192,15 @@
"newDataInvalid": "New invite code data is invalid."
}
},
"errors": {
"failedToFetchActiveInvite": "Failed to fetch active invite code.",
"failedToFetchGroupDetails": "Failed to fetch group details.",
"failedToLoadUpcomingChores": "Error loading upcoming chores:",
"failedToLoadRecentExpenses": "Error loading recent expenses:"
},
"console": {
"noActiveInvite": "No active invite code found for this group."
},
"chores": {
"title": "Group Chores",
"manageButton": "Manage Chores",
@ -191,6 +218,8 @@
"settleShareButton": "Settle My Share",
"activityLabel": "Activity:",
"byUser": "by",
"fallbackUserName": "User ID: {userId}",
"activityByUserFallback": "User {userId}",
"splitTypes": {
"equal": "Equal",
"exactAmounts": "Exact Amounts",

View File

@ -73,7 +73,10 @@
"groupNameRequired": "ES: Group name is required",
"createFailed": "ES: Failed to create group. Please try again.",
"inviteCodeRequired": "ES: Invite code is required",
"joinFailed": "ES: Failed to join group. Please check the invite code and try again."
"joinFailed": "ES: Failed to join group. Please check the invite code and try again.",
"invalidDataFromServer": "[TRANSLATE] Invalid data received from server.",
"createFailedConsole": "[TRANSLATE] Error creating group:",
"joinFailedConsole": "[TRANSLATE] Error joining group:"
},
"notifications": {
"groupCreatedSuccess": "ES: Group '{groupName}' created successfully.",
@ -85,7 +88,8 @@
"authCallbackPage": {
"redirecting": "ES: Redirecting...",
"errors": {
"authenticationFailed": "ES: Authentication failed"
"authenticationFailed": "ES: Authentication failed",
"noTokenProvided": "[TRANSLATE] No token provided"
}
},
"choresPage": {
@ -165,7 +169,17 @@
"quickDueDateTomorrow": "ES: Tomorrow",
"quickDueDateNextWeek": "ES: Next Week",
"cancelButton": "ES: Cancel",
"saveButton": "ES: Save"
"saveButton": "ES: Save",
"intervalPlaceholder": "[TRANSLATE] e.g., 10"
},
"consoleErrors": {
"loadFailed": "[TRANSLATE] Failed to load all chores:",
"loadGroupsFailed": "[TRANSLATE] Failed to load groups",
"createAssignmentForNewChoreFailed": "[TRANSLATE] Failed to create assignment for new chore:",
"saveFailed": "[TRANSLATE] Failed to save chore:",
"deleteFailed": "[TRANSLATE] Failed to delete chore:",
"createAssignmentFailed": "[TRANSLATE] Failed to create assignment:",
"updateCompletionStatusFailed": "[TRANSLATE] Failed to update chore completion status:"
},
"deleteDialog": {
"title": "ES: Delete Chore",
@ -228,10 +242,14 @@
"title": "ES: Group Members",
"defaultRole": "ES: Member",
"removeButton": "ES: Remove",
"emptyState": "ES: No members found."
"emptyState": "ES: No members found.",
"closeMenuLabel": "[TRANSLATE] Close menu"
},
"invites": {
"title": "ES: Invite Members",
"description": "[TRANSLATE] Invite new members by generating a shareable code.",
"addMemberButtonLabel": "[TRANSLATE] Add member",
"closeInviteLabel": "[TRANSLATE] Close invite",
"regenerateButton": "ES: Regenerate Invite Code",
"generateButton": "ES: Generate Invite Code",
"activeCodeLabel": "ES: Current Active Invite Code:",
@ -242,6 +260,15 @@
"newDataInvalid": "ES: New invite code data is invalid."
}
},
"errors": {
"failedToFetchActiveInvite": "[TRANSLATE] Failed to fetch active invite code.",
"failedToFetchGroupDetails": "[TRANSLATE] Failed to fetch group details.",
"failedToLoadUpcomingChores": "[TRANSLATE] Error loading upcoming chores:",
"failedToLoadRecentExpenses": "[TRANSLATE] Error loading recent expenses:"
},
"console": {
"noActiveInvite": "[TRANSLATE] No active invite code found for this group."
},
"chores": {
"title": "ES: Group Chores",
"manageButton": "ES: Manage Chores",
@ -252,6 +279,8 @@
"title": "ES: Group Expenses",
"manageButton": "ES: Manage Expenses",
"emptyState": "ES: No expenses recorded. Click \"Manage Expenses\" to add some!",
"fallbackUserName": "[TRANSLATE] User ID: {userId}",
"activityByUserFallback": "[TRANSLATE] User {userId}",
"splitTypes": {
"equal": "ES: Equal",
"exactAmounts": "ES: Exact Amounts",

View File

@ -73,7 +73,10 @@
"groupNameRequired": "FR: Group name is required",
"createFailed": "FR: Failed to create group. Please try again.",
"inviteCodeRequired": "FR: Invite code is required",
"joinFailed": "FR: Failed to join group. Please check the invite code and try again."
"joinFailed": "FR: Failed to join group. Please check the invite code and try again.",
"invalidDataFromServer": "[TRANSLATE] Invalid data received from server.",
"createFailedConsole": "[TRANSLATE] Error creating group:",
"joinFailedConsole": "[TRANSLATE] Error joining group:"
},
"notifications": {
"groupCreatedSuccess": "FR: Group '{groupName}' created successfully.",
@ -85,7 +88,8 @@
"authCallbackPage": {
"redirecting": "FR: Redirecting...",
"errors": {
"authenticationFailed": "FR: Authentication failed"
"authenticationFailed": "FR: Authentication failed",
"noTokenProvided": "[TRANSLATE] No token provided"
}
},
"choresPage": {
@ -165,7 +169,17 @@
"quickDueDateTomorrow": "FR: Tomorrow",
"quickDueDateNextWeek": "FR: Next Week",
"cancelButton": "FR: Cancel",
"saveButton": "FR: Save"
"saveButton": "FR: Save",
"intervalPlaceholder": "[TRANSLATE] e.g., 10"
},
"consoleErrors": {
"loadFailed": "[TRANSLATE] Failed to load all chores:",
"loadGroupsFailed": "[TRANSLATE] Failed to load groups",
"createAssignmentForNewChoreFailed": "[TRANSLATE] Failed to create assignment for new chore:",
"saveFailed": "[TRANSLATE] Failed to save chore:",
"deleteFailed": "[TRANSLATE] Failed to delete chore:",
"createAssignmentFailed": "[TRANSLATE] Failed to create assignment:",
"updateCompletionStatusFailed": "[TRANSLATE] Failed to update chore completion status:"
},
"deleteDialog": {
"title": "FR: Delete Chore",
@ -228,10 +242,14 @@
"title": "FR: Group Members",
"defaultRole": "FR: Member",
"removeButton": "FR: Remove",
"emptyState": "FR: No members found."
"emptyState": "FR: No members found.",
"closeMenuLabel": "[TRANSLATE] Close menu"
},
"invites": {
"title": "FR: Invite Members",
"description": "[TRANSLATE] Invite new members by generating a shareable code.",
"addMemberButtonLabel": "[TRANSLATE] Add member",
"closeInviteLabel": "[TRANSLATE] Close invite",
"regenerateButton": "FR: Regenerate Invite Code",
"generateButton": "FR: Generate Invite Code",
"activeCodeLabel": "FR: Current Active Invite Code:",
@ -242,6 +260,15 @@
"newDataInvalid": "FR: New invite code data is invalid."
}
},
"errors": {
"failedToFetchActiveInvite": "[TRANSLATE] Failed to fetch active invite code.",
"failedToFetchGroupDetails": "[TRANSLATE] Failed to fetch group details.",
"failedToLoadUpcomingChores": "[TRANSLATE] Error loading upcoming chores:",
"failedToLoadRecentExpenses": "[TRANSLATE] Error loading recent expenses:"
},
"console": {
"noActiveInvite": "[TRANSLATE] No active invite code found for this group."
},
"chores": {
"title": "FR: Group Chores",
"manageButton": "FR: Manage Chores",
@ -252,6 +279,8 @@
"title": "FR: Group Expenses",
"manageButton": "FR: Manage Expenses",
"emptyState": "FR: No expenses recorded. Click \"Manage Expenses\" to add some!",
"fallbackUserName": "[TRANSLATE] User ID: {userId}",
"activityByUserFallback": "[TRANSLATE] User {userId}",
"splitTypes": {
"equal": "FR: Equal",
"exactAmounts": "FR: Exact Amounts",

View File

@ -38,7 +38,7 @@ onMounted(async () => {
const tokenToUse = accessToken || legacyToken;
if (!tokenToUse) {
throw new Error('No token provided');
throw new Error(t('authCallbackPage.errors.noTokenProvided'));
}
await authStore.setTokens({ access_token: tokenToUse, refresh_token: refreshToken });

View File

@ -80,7 +80,7 @@ const loadChores = async () => {
cachedChores.value = mappedChores;
cachedTimestamp.value = Date.now()
} catch (error) {
console.error('Failed to load all chores:', error)
console.error(t('choresPage.consoleErrors.loadFailed'), error)
notificationStore.addNotification({ message: t('choresPage.notifications.loadFailed', 'Failed to load chores.'), type: 'error' })
} finally {
isLoading.value = false
@ -91,7 +91,7 @@ const loadGroups = async () => {
try {
groups.value = await groupService.getUserGroups();
} catch (error) {
console.error("Failed to load groups", error);
console.error(t('choresPage.consoleErrors.loadGroupsFailed'), error);
notificationStore.addNotification({ message: t('choresPage.notifications.loadGroupsFailed', 'Failed to load groups.'), type: 'error' });
}
}
@ -227,7 +227,7 @@ const handleFormSubmit = async () => {
due_date: createdChore.next_due_date
});
} catch (assignmentError) {
console.error('Failed to create assignment for new chore:', assignmentError);
console.error(t('choresPage.consoleErrors.createAssignmentForNewChoreFailed'), assignmentError);
// Continue anyway since the chore was created
}
}
@ -237,7 +237,7 @@ const handleFormSubmit = async () => {
showChoreModal.value = false;
await loadChores();
} catch (error) {
console.error('Failed to save chore:', error);
console.error(t('choresPage.consoleErrors.saveFailed'), error);
notificationStore.addNotification({ message: t('choresPage.notifications.saveFailed', 'Failed to save the chore.'), type: 'error' });
}
}
@ -255,7 +255,7 @@ const deleteChore = async () => {
showDeleteDialog.value = false
await loadChores()
} catch (error) {
console.error('Failed to delete chore:', error)
console.error(t('choresPage.consoleErrors.deleteFailed'), error)
notificationStore.addNotification({ message: t('choresPage.notifications.deleteFailed', 'Failed to delete chore.'), type: 'error' })
}
}
@ -271,7 +271,7 @@ const toggleCompletion = async (chore: ChoreWithCompletion) => {
});
chore.current_assignment_id = assignment.id;
} catch (error) {
console.error('Failed to create assignment:', error);
console.error(t('choresPage.consoleErrors.createAssignmentFailed'), error);
notificationStore.addNotification({
message: t('choresPage.notifications.createAssignmentFailed', 'Failed to create assignment for chore.'),
type: 'error'
@ -299,7 +299,7 @@ const toggleCompletion = async (chore: ChoreWithCompletion) => {
});
await loadChores();
} catch (error) {
console.error('Failed to update chore completion status:', error);
console.error(t('choresPage.consoleErrors.updateCompletionStatusFailed'), error);
notificationStore.addNotification({ message: t('choresPage.notifications.updateFailed', 'Failed to update chore status.'), type: 'error' });
chore.is_completed = originalCompleted;
} finally {
@ -403,7 +403,7 @@ const toggleCompletion = async (chore: ChoreWithCompletion) => {
<label class="form-label" for="chore-interval">{{ t('choresPage.form.interval', 'Interval (days)')
}}</label>
<input id="chore-interval" type="number" v-model.number="choreForm.custom_interval_days"
class="form-input" placeholder="e.g., 10" min="1">
class="form-input" :placeholder="t('choresPage.form.intervalPlaceholder')" min="1">
</div>
<div class="form-group">
<label class="form-label">{{ t('choresPage.form.type', 'Type') }}</label>

View File

@ -21,7 +21,7 @@
<div class="popup-header">
<span class="font-semibold truncate">{{ member.email }}</span>
<VButton variant="neutral" size="sm" :icon-only="true" iconLeft="x" @click="activeMemberMenu = null"
aria-label="Close menu" />
:aria-label="t('groupDetailPage.members.closeMenuLabel')" />
</div>
<div class="member-menu-content">
<VBadge :text="member.role || t('groupDetailPage.members.defaultRole')"
@ -37,8 +37,7 @@
</div>
<button ref="addMemberButtonRef" @click="toggleInviteUI" class="add-member-btn"
:aria-label="t('groupDetailPage.invites.title')">
<!-- <VIcon name="plus" size="md" /> -->
+
{{ t('groupDetailPage.invites.addMemberButtonLabel') }}
</button>
<!-- Invite Members Popup -->
@ -47,9 +46,9 @@
<VHeading :level="3" class="!m-0 !p-0 !border-none">{{ t('groupDetailPage.invites.title') }}
</VHeading>
<VButton variant="neutral" size="sm" :icon-only="true" iconLeft="x" @click="showInviteUI = false"
aria-label="Close invite" />
:aria-label="t('groupDetailPage.invites.closeInviteLabel')" />
</div>
<p class="text-sm text-gray-500 my-2">Invite new members by generating a shareable code.</p>
<p class="text-sm text-gray-500 my-2">{{ t('groupDetailPage.invites.description') }}</p>
<VButton variant="primary" class="w-full" @click="generateInviteCode" :disabled="generatingInvite">
<VSpinner v-if="generatingInvite" size="sm" /> {{ inviteCode ?
t('groupDetailPage.invites.regenerateButton') :
@ -146,7 +145,7 @@
<div class="neo-splits-list">
<div v-for="split in expense.splits" :key="split.id" class="neo-split-item">
<div class="split-col split-user">
<strong>{{ split.user?.name || split.user?.email || `User ID: ${split.user_id}` }}</strong>
<strong>{{ split.user?.name || split.user?.email || t('groupDetailPage.expenses.fallbackUserName', { userId: split.user_id }) }}</strong>
</div>
<div class="split-col split-owes">
{{ t('groupDetailPage.expenses.owes') }} <strong>{{
@ -178,8 +177,7 @@
{{ t('groupDetailPage.expenses.activityLabel') }} {{
formatCurrency(activity.amount_paid) }}
{{
t('groupDetailPage.expenses.byUser') }} {{ activity.payer?.name || `User
${activity.paid_by_user_id}` }} {{ t('groupDetailPage.expenses.onDate') }} {{ new
t('groupDetailPage.expenses.byUser') }} {{ activity.payer?.name || t('groupDetailPage.expenses.activityByUserFallback', { userId: activity.paid_by_user_id }) }} {{ t('groupDetailPage.expenses.onDate') }} {{ new
Date(activity.paid_at).toLocaleDateString() }}
</li>
</ul>
@ -209,7 +207,7 @@
<div v-else>
<p>{{ t('groupDetailPage.settleShareModal.settleAmountFor', {
userName: selectedSplitForSettlement?.user?.name
|| selectedSplitForSettlement?.user?.email || `User ID: ${selectedSplitForSettlement?.user_id}`
|| selectedSplitForSettlement?.user?.email || t('groupDetailPage.expenses.fallbackUserName', { userId: selectedSplitForSettlement?.user_id })
}) }}</p>
<VFormField :label="t('groupDetailPage.settleShareModal.amountLabel')"
:error-message="settleAmountError || undefined">
@ -383,9 +381,9 @@ const fetchActiveInviteCode = async () => {
inviteCode.value = null; // Explicitly set to null on 404
inviteExpiresAt.value = null;
// Optional: notify user or set a flag to show "generate one" message more prominently
console.info('No active invite code found for this group.');
console.info(t('groupDetailPage.console.noActiveInvite'));
} else {
const message = err instanceof Error ? err.message : 'Failed to fetch active invite code.';
const message = err instanceof Error ? err.message : t('groupDetailPage.errors.failedToFetchActiveInvite');
// error.value = message; // This would display a large error banner, might be too much
console.error('Error fetching active invite code:', err);
notificationStore.addNotification({ message, type: 'error' });
@ -418,7 +416,7 @@ const fetchGroupDetails = async () => {
timestamp: Date.now(),
};
} catch (err: unknown) {
const message = err instanceof Error ? err.message : 'Failed to fetch group details.';
const message = err instanceof Error ? err.message : t('groupDetailPage.errors.failedToFetchGroupDetails');
// Only show the main error banner if we have no data at all to show
if (!group.value) {
error.value = message;
@ -524,7 +522,7 @@ const loadUpcomingChores = async () => {
timestamp: Date.now()
};
} catch (error) {
console.error('Error loading upcoming chores:', error)
console.error(t('groupDetailPage.errors.failedToLoadUpcomingChores'), error)
}
}
@ -563,7 +561,7 @@ const loadRecentExpenses = async () => {
)
recentExpenses.value = response.data
} catch (error) {
console.error('Error loading recent expenses:', error)
console.error(t('groupDetailPage.errors.failedToLoadRecentExpenses'), error)
notificationStore.addNotification({ message: t('groupDetailPage.notifications.loadExpensesFailed'), type: 'error' });
}
}

View File

@ -281,12 +281,12 @@ const handleCreateGroup = async () => {
cachedGroups.value = groups.value;
cachedTimestamp.value = Date.now();
} else {
throw new Error('Invalid data received from server.');
throw new Error(t('groupsPage.errors.invalidDataFromServer'));
}
} catch (error: any) {
const message = error.response?.data?.detail || (error instanceof Error ? error.message : t('groupsPage.errors.createFailed'));
createGroupFormError.value = message;
console.error('Error creating group:', error);
console.error(t('groupsPage.errors.createFailedConsole'), error);
notificationStore.addNotification({ message, type: 'error' });
} finally {
creatingGroup.value = false;
@ -327,7 +327,7 @@ const handleJoinGroup = async () => {
} catch (error: any) {
const message = error.response?.data?.detail || (error instanceof Error ? error.message : t('groupsPage.errors.joinFailed'));
joinGroupFormError.value = message;
console.error('Error joining group:', error);
console.error(t('groupsPage.errors.joinFailedConsole'), error);
notificationStore.addNotification({ message, type: 'error' });
} finally {
joiningGroup.value = false;