Merge pull request #16 from whtvrboo/i18n-pages-partial

feat: Add missing i18n translations for page components (partial)
This commit is contained in:
whtvrboo 2025-06-07 22:41:18 +02:00 committed by GitHub
commit b0ec84b8ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 156 additions and 42 deletions

View File

@ -73,7 +73,10 @@
"groupNameRequired": "DE: Group name is required", "groupNameRequired": "DE: Group name is required",
"createFailed": "DE: Failed to create group. Please try again.", "createFailed": "DE: Failed to create group. Please try again.",
"inviteCodeRequired": "DE: Invite code is required", "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": { "notifications": {
"groupCreatedSuccess": "DE: Group '{groupName}' created successfully.", "groupCreatedSuccess": "DE: Group '{groupName}' created successfully.",
@ -85,7 +88,8 @@
"authCallbackPage": { "authCallbackPage": {
"redirecting": "DE: Redirecting...", "redirecting": "DE: Redirecting...",
"errors": { "errors": {
"authenticationFailed": "DE: Authentication failed" "authenticationFailed": "DE: Authentication failed",
"noTokenProvided": "[TRANSLATE] No token provided"
} }
}, },
"choresPage": { "choresPage": {
@ -165,7 +169,17 @@
"quickDueDateTomorrow": "DE: Tomorrow", "quickDueDateTomorrow": "DE: Tomorrow",
"quickDueDateNextWeek": "DE: Next Week", "quickDueDateNextWeek": "DE: Next Week",
"cancelButton": "DE: Cancel", "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": { "deleteDialog": {
"title": "DE: Delete Chore", "title": "DE: Delete Chore",
@ -228,10 +242,14 @@
"title": "DE: Group Members", "title": "DE: Group Members",
"defaultRole": "DE: Member", "defaultRole": "DE: Member",
"removeButton": "DE: Remove", "removeButton": "DE: Remove",
"emptyState": "DE: No members found." "emptyState": "DE: No members found.",
"closeMenuLabel": "[TRANSLATE] Close menu"
}, },
"invites": { "invites": {
"title": "DE: Invite Members", "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", "regenerateButton": "DE: Regenerate Invite Code",
"generateButton": "DE: Generate Invite Code", "generateButton": "DE: Generate Invite Code",
"activeCodeLabel": "DE: Current Active Invite Code:", "activeCodeLabel": "DE: Current Active Invite Code:",
@ -242,6 +260,15 @@
"newDataInvalid": "DE: New invite code data is invalid." "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": { "chores": {
"title": "DE: Group Chores", "title": "DE: Group Chores",
"manageButton": "DE: Manage Chores", "manageButton": "DE: Manage Chores",
@ -252,6 +279,8 @@
"title": "DE: Group Expenses", "title": "DE: Group Expenses",
"manageButton": "DE: Manage Expenses", "manageButton": "DE: Manage Expenses",
"emptyState": "DE: No expenses recorded. Click \"Manage Expenses\" to add some!", "emptyState": "DE: No expenses recorded. Click \"Manage Expenses\" to add some!",
"fallbackUserName": "[TRANSLATE] User ID: {userId}",
"activityByUserFallback": "[TRANSLATE] User {userId}",
"splitTypes": { "splitTypes": {
"equal": "DE: Equal", "equal": "DE: Equal",
"exactAmounts": "DE: Exact Amounts", "exactAmounts": "DE: Exact Amounts",

View File

@ -73,7 +73,10 @@
"groupNameRequired": "Group name is required", "groupNameRequired": "Group name is required",
"createFailed": "Failed to create group. Please try again.", "createFailed": "Failed to create group. Please try again.",
"inviteCodeRequired": "Invite code is required", "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": { "notifications": {
"groupCreatedSuccess": "Group '{groupName}' created successfully.", "groupCreatedSuccess": "Group '{groupName}' created successfully.",
@ -85,7 +88,8 @@
"authCallbackPage": { "authCallbackPage": {
"redirecting": "Redirecting...", "redirecting": "Redirecting...",
"errors": { "errors": {
"authenticationFailed": "Authentication failed" "authenticationFailed": "Authentication failed",
"noTokenProvided": "No token provided"
} }
}, },
"choresPage": { "choresPage": {
@ -125,7 +129,17 @@
"save": "Save Changes", "save": "Save Changes",
"create": "Create", "create": "Create",
"editChore": "Edit Chore", "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": { "deleteConfirm": {
"title": "Confirm Deletion", "title": "Confirm Deletion",
@ -160,10 +174,14 @@
"title": "Group Members", "title": "Group Members",
"defaultRole": "Member", "defaultRole": "Member",
"removeButton": "Remove", "removeButton": "Remove",
"emptyState": "No members found." "emptyState": "No members found.",
"closeMenuLabel": "Close menu"
}, },
"invites": { "invites": {
"title": "Invite Members", "title": "Invite Members",
"description": "Invite new members by generating a shareable code.",
"addMemberButtonLabel": "Add member",
"closeInviteLabel": "Close invite",
"regenerateButton": "Regenerate Invite Code", "regenerateButton": "Regenerate Invite Code",
"generateButton": "Generate Invite Code", "generateButton": "Generate Invite Code",
"activeCodeLabel": "Current Active Invite Code:", "activeCodeLabel": "Current Active Invite Code:",
@ -174,6 +192,15 @@
"newDataInvalid": "New invite code data is invalid." "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": { "chores": {
"title": "Group Chores", "title": "Group Chores",
"manageButton": "Manage Chores", "manageButton": "Manage Chores",
@ -191,6 +218,8 @@
"settleShareButton": "Settle My Share", "settleShareButton": "Settle My Share",
"activityLabel": "Activity:", "activityLabel": "Activity:",
"byUser": "by", "byUser": "by",
"fallbackUserName": "User ID: {userId}",
"activityByUserFallback": "User {userId}",
"splitTypes": { "splitTypes": {
"equal": "Equal", "equal": "Equal",
"exactAmounts": "Exact Amounts", "exactAmounts": "Exact Amounts",

View File

@ -73,7 +73,10 @@
"groupNameRequired": "ES: Group name is required", "groupNameRequired": "ES: Group name is required",
"createFailed": "ES: Failed to create group. Please try again.", "createFailed": "ES: Failed to create group. Please try again.",
"inviteCodeRequired": "ES: Invite code is required", "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": { "notifications": {
"groupCreatedSuccess": "ES: Group '{groupName}' created successfully.", "groupCreatedSuccess": "ES: Group '{groupName}' created successfully.",
@ -85,7 +88,8 @@
"authCallbackPage": { "authCallbackPage": {
"redirecting": "ES: Redirecting...", "redirecting": "ES: Redirecting...",
"errors": { "errors": {
"authenticationFailed": "ES: Authentication failed" "authenticationFailed": "ES: Authentication failed",
"noTokenProvided": "[TRANSLATE] No token provided"
} }
}, },
"choresPage": { "choresPage": {
@ -165,7 +169,17 @@
"quickDueDateTomorrow": "ES: Tomorrow", "quickDueDateTomorrow": "ES: Tomorrow",
"quickDueDateNextWeek": "ES: Next Week", "quickDueDateNextWeek": "ES: Next Week",
"cancelButton": "ES: Cancel", "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": { "deleteDialog": {
"title": "ES: Delete Chore", "title": "ES: Delete Chore",
@ -228,10 +242,14 @@
"title": "ES: Group Members", "title": "ES: Group Members",
"defaultRole": "ES: Member", "defaultRole": "ES: Member",
"removeButton": "ES: Remove", "removeButton": "ES: Remove",
"emptyState": "ES: No members found." "emptyState": "ES: No members found.",
"closeMenuLabel": "[TRANSLATE] Close menu"
}, },
"invites": { "invites": {
"title": "ES: Invite Members", "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", "regenerateButton": "ES: Regenerate Invite Code",
"generateButton": "ES: Generate Invite Code", "generateButton": "ES: Generate Invite Code",
"activeCodeLabel": "ES: Current Active Invite Code:", "activeCodeLabel": "ES: Current Active Invite Code:",
@ -242,6 +260,15 @@
"newDataInvalid": "ES: New invite code data is invalid." "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": { "chores": {
"title": "ES: Group Chores", "title": "ES: Group Chores",
"manageButton": "ES: Manage Chores", "manageButton": "ES: Manage Chores",
@ -252,6 +279,8 @@
"title": "ES: Group Expenses", "title": "ES: Group Expenses",
"manageButton": "ES: Manage Expenses", "manageButton": "ES: Manage Expenses",
"emptyState": "ES: No expenses recorded. Click \"Manage Expenses\" to add some!", "emptyState": "ES: No expenses recorded. Click \"Manage Expenses\" to add some!",
"fallbackUserName": "[TRANSLATE] User ID: {userId}",
"activityByUserFallback": "[TRANSLATE] User {userId}",
"splitTypes": { "splitTypes": {
"equal": "ES: Equal", "equal": "ES: Equal",
"exactAmounts": "ES: Exact Amounts", "exactAmounts": "ES: Exact Amounts",

View File

@ -73,7 +73,10 @@
"groupNameRequired": "FR: Group name is required", "groupNameRequired": "FR: Group name is required",
"createFailed": "FR: Failed to create group. Please try again.", "createFailed": "FR: Failed to create group. Please try again.",
"inviteCodeRequired": "FR: Invite code is required", "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": { "notifications": {
"groupCreatedSuccess": "FR: Group '{groupName}' created successfully.", "groupCreatedSuccess": "FR: Group '{groupName}' created successfully.",
@ -85,7 +88,8 @@
"authCallbackPage": { "authCallbackPage": {
"redirecting": "FR: Redirecting...", "redirecting": "FR: Redirecting...",
"errors": { "errors": {
"authenticationFailed": "FR: Authentication failed" "authenticationFailed": "FR: Authentication failed",
"noTokenProvided": "[TRANSLATE] No token provided"
} }
}, },
"choresPage": { "choresPage": {
@ -165,7 +169,17 @@
"quickDueDateTomorrow": "FR: Tomorrow", "quickDueDateTomorrow": "FR: Tomorrow",
"quickDueDateNextWeek": "FR: Next Week", "quickDueDateNextWeek": "FR: Next Week",
"cancelButton": "FR: Cancel", "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": { "deleteDialog": {
"title": "FR: Delete Chore", "title": "FR: Delete Chore",
@ -228,10 +242,14 @@
"title": "FR: Group Members", "title": "FR: Group Members",
"defaultRole": "FR: Member", "defaultRole": "FR: Member",
"removeButton": "FR: Remove", "removeButton": "FR: Remove",
"emptyState": "FR: No members found." "emptyState": "FR: No members found.",
"closeMenuLabel": "[TRANSLATE] Close menu"
}, },
"invites": { "invites": {
"title": "FR: Invite Members", "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", "regenerateButton": "FR: Regenerate Invite Code",
"generateButton": "FR: Generate Invite Code", "generateButton": "FR: Generate Invite Code",
"activeCodeLabel": "FR: Current Active Invite Code:", "activeCodeLabel": "FR: Current Active Invite Code:",
@ -242,6 +260,15 @@
"newDataInvalid": "FR: New invite code data is invalid." "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": { "chores": {
"title": "FR: Group Chores", "title": "FR: Group Chores",
"manageButton": "FR: Manage Chores", "manageButton": "FR: Manage Chores",
@ -252,6 +279,8 @@
"title": "FR: Group Expenses", "title": "FR: Group Expenses",
"manageButton": "FR: Manage Expenses", "manageButton": "FR: Manage Expenses",
"emptyState": "FR: No expenses recorded. Click \"Manage Expenses\" to add some!", "emptyState": "FR: No expenses recorded. Click \"Manage Expenses\" to add some!",
"fallbackUserName": "[TRANSLATE] User ID: {userId}",
"activityByUserFallback": "[TRANSLATE] User {userId}",
"splitTypes": { "splitTypes": {
"equal": "FR: Equal", "equal": "FR: Equal",
"exactAmounts": "FR: Exact Amounts", "exactAmounts": "FR: Exact Amounts",

View File

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

View File

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

View File

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

View File

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