-
Notifications
You must be signed in to change notification settings - Fork 1
feat(account): add forgot password page #147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/v0.2
Are you sure you want to change the base?
Conversation
…5-login-modal # Conflicts: # bun.lock
# Conflicts: # bun.lock # components/account/AccountLoginInner.vue # components/ui/alert/index.ts # i18n/locales/de-DE/account.json # i18n/locales/en-GB/account.json # package.json # stories/Alert.story.vue
Co-authored-by: nele sophie <[email protected]>
…ssword # Conflicts: # components/account/AccountLoginInner.vue
WalkthroughThis update introduces a new account recovery feature with localized UI and documentation. It adds three new Vue components for password recovery, updates translation keys for login and recovery forms, enhances alert styling with a "successful" variant, and provides documentation for the new feature. No changes were made to exported logic or public APIs. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant AccountRecoverInner
participant AccountRecover
participant CustomerStore
participant EmailService
User->>AccountRecoverInner: Submit recovery form (email)
AccountRecoverInner->>AccountRecover: emit recover(email)
AccountRecover->>CustomerStore: resetPassword(email, storefrontUrl)
CustomerStore->>EmailService: Send recovery email if email registered
EmailService-->>CustomerStore: Confirmation (success or silent failure)
CustomerStore-->>AccountRecover: Resolve/reset
AccountRecover->>AccountRecoverInner: Set showSuccessMessage = true
AccountRecoverInner->>User: Show success message
Poem
Note ⚡️ AI Code Reviews for VS Code, Cursor, WindsurfCodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback. Note ⚡️ Faster reviews with cachingCodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure ✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
…ssword # Conflicts: # i18n/locales/de-DE/account.json # i18n/locales/en-GB/account.json
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
components/account/AccountRecoverInner.vue (1)
35-62
: Consider adding paragraphs to improve text readability.The information text at line 61 is a single paragraph. For better readability, consider breaking it into multiple paragraphs if the text is lengthy.
🧹 Nitpick comments (2)
components/account/AccountRecover.vue (1)
10-25
: Consider adding error logging for debugging purposes.While hiding errors from users is appropriate for security (preventing account enumeration), completely swallowing errors without logging could make debugging difficult in production.
try { await customerStore.resetPassword({ ...recoverData, storefrontUrl: getStorefrontUrl(), }); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (error) { - // catch but do nothing + // Don't show the error to the user to prevent account enumeration + // but log it for debugging purposes + console.error('Password reset error:', error); } finally { showSuccessMessage.value = true; isLoading.value = false; }components/account/AccountRecoverInner.vue (1)
1-98
: Consider adding ARIA attributes for improved accessibility.The component could benefit from additional accessibility attributes to ensure it's usable by everyone, including those using screen readers.
Consider adding:
aria-live="polite"
to the alert region for screen readers to announce status changesaria-labelledby
to connect form elements with their labelsrole="status"
to the success messageExample for the success alert:
-<UiAlert variant="successful" class="mb-4 flex gap-4"> +<UiAlert variant="successful" class="mb-4 flex gap-4" role="status" aria-live="polite">
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
📒 Files selected for processing (9)
components/account/AccountLoginInner.vue
(2 hunks)components/account/AccountRecover.vue
(1 hunks)components/account/AccountRecoverInner.vue
(1 hunks)components/ui/alert/index.ts
(1 hunks)docs/features/account-management.md
(1 hunks)i18n/locales/de-DE/account.json
(2 hunks)i18n/locales/en-GB/account.json
(1 hunks)pages/account/recover.vue
(1 hunks)stories/Alert.story.vue
(3 hunks)
🔇 Additional comments (23)
components/ui/alert/index.ts (1)
14-14
: Good addition of the successful variantAdding a green-themed "successful" variant to the alert component is a logical complement to the existing destructive variant. This will help provide clear, visual feedback for successful operations like password reset confirmations.
stories/Alert.story.vue (3)
3-3
: Good improvement to icon stylingAdding the
shrink-0
class prevents the icon from collapsing in the flex container, ensuring consistent layout across all alert variants.
13-14
: Appropriate spacing and consistency fixesAdding the bottom margin to the destructive alert and the
shrink-0
class to its icon improves layout consistency between alert variants.
24-33
: Well-implemented successful variant demoThe new successful variant alert example provides a clear demonstration of how to use this variant in the application. This aligns perfectly with the added "successful" variant in the alert component configuration.
i18n/locales/de-DE/account.json (2)
16-27
: Good restructuring of email and password fieldsMoving email and password fields out of the "login" nested object makes them more reusable across different account-related features, reducing duplication. This addresses the previous discussion about duplicated strings, making the localization more maintainable.
28-35
: Well-structured localization for password recoveryThe new "recover" section contains all necessary translations for the password recovery feature, following the existing patterns in the file. The content covers all aspects of the recovery flow: header, information text, button labels, and success messaging.
components/account/AccountLoginInner.vue (3)
24-24
: Correctly updated validation error message keysThe validation error message translation keys have been updated to use the more generic path structure, maintaining consistency with the restructured localization file.
Also applies to: 30-30
46-50
: Correctly updated form field translation keysAll form field labels and placeholders now use the restructured translation key paths, maintaining consistency with the localization changes.
Also applies to: 53-57
64-66
: Properly implemented password recovery linkThe forgotten password link now correctly points to "/account/recover" instead of the placeholder "/account/todo", and uses the updated translation key. This establishes the connection between the login form and the new password recovery feature.
pages/account/recover.vue (1)
1-7
: Well-structured responsive layout for account recovery page.The component provides a clean and responsive layout container for the AccountRecover functionality. The responsive width classes (w-full, md:w-2/3, xl:w-1/2) ensure the recovery form displays appropriately across different device sizes.
i18n/locales/en-GB/account.json (2)
16-27
: Good restructuring of account related translations.The flattening of previously nested login keys for email and password makes the translation structure more consistent and easier to maintain.
28-35
: Complete set of translations for the recovery feature.All necessary translation keys for the password recovery feature are provided, including headers, information text, button labels, and success messages. The generic success message helps prevent account enumeration, which is a good security practice.
components/account/AccountRecover.vue (2)
1-9
: Good initial setup with clear state management.The component properly initializes reactive state variables for tracking loading state and success message visibility, and imports the necessary dependencies.
28-34
: Clean component composition with appropriate props binding.The template correctly renders the AccountRecoverInner component, passing the appropriate reactive state as props and handling the recover event.
docs/features/account-management.md (6)
1-9
: Clear introduction and security considerations.The documentation provides a clear introduction to the Account Recovery feature and correctly highlights the security approach of showing a generic success message regardless of email registration status to prevent account enumeration.
10-19
: Well-structured component hierarchy description.The documentation clearly explains the layered component structure, making it easy for developers to understand how the recovery feature is organized.
22-37
: Comprehensive documentation of customization options.The named slots table provides developers with clear information about all customization points available in the AccountRecoverInner component.
40-56
: Clear documentation of props and events.The props and events sections clearly document the interface of the AccountRecoverInner component, making it easy for developers to understand how to use it.
59-76
: Helpful usage example for customization.The example demonstrates practical customization of the recovery component, helping developers understand how to use the named slots effectively.
78-82
: Good technical context and external reference.The technical notes provide additional context about the implementation and include a useful link to the related Shopware Frontends documentation.
components/account/AccountRecoverInner.vue (3)
1-33
: Well-structured script section with proper TypeScript typing.The component uses TypeScript properly with zod validation and has a clean organization of props, emits, and schema definition. The RecoverData type export is a good practice for type safety.
40-52
: Success alert implementation looks good.The success alert is well implemented with proper structure, icons, and localized content. The "successful" variant provides clear visual feedback to users.
64-95
: Form implementation is clean with good UX considerations.The form is well-structured with:
- Proper validation schema integration
- Localized field labels and placeholders
- Clear button actions with loading state
- Good responsive design with flex layout
The button layout with grow properties ensures they work well on different screen sizes.
<script setup lang="ts"> | ||
import * as z from 'zod'; | ||
const { t } = useI18n(); | ||
|
||
withDefaults( | ||
defineProps<{ | ||
isLoading?: boolean; | ||
showSuccessMessage?: boolean; | ||
}>(), | ||
{ | ||
isLoading: false, | ||
showSuccessMessage: false, | ||
}, | ||
); | ||
|
||
const emits = defineEmits<{ | ||
recover: [recoverData: RecoverData]; | ||
}>(); | ||
|
||
const schema = z.object({ | ||
email: z | ||
.string({ | ||
required_error: t('account.email.error'), | ||
}) | ||
.email(), | ||
}); | ||
|
||
export type RecoverData = z.infer<typeof schema>; | ||
|
||
const recover = (recoverData: RecoverData) => { | ||
emits('recover', recoverData); | ||
}; | ||
</script> | ||
|
||
<template> | ||
<slot name="wrapper"> | ||
<div v-auto-animate> | ||
<slot name="success-message"> | ||
<template v-if="showSuccessMessage"> | ||
<UiAlert variant="successful" class="mb-4 flex gap-4"> | ||
<slot name="alert-icon"> | ||
<Icon name="mdi:check" class="size-4 shrink-0" /> | ||
</slot> | ||
|
||
<div> | ||
<UiAlertTitle>{{ $t('account.recover.successHeader') }}</UiAlertTitle> | ||
<UiAlertDescription> | ||
{{ $t('account.recover.successMessage') }} | ||
</UiAlertDescription> | ||
</div> | ||
</UiAlert> | ||
</template> | ||
</slot> | ||
|
||
<slot name="header"> | ||
<h1 class="text-lg font-semibold">{{ $t('account.recover.header') }}</h1> | ||
<hr> | ||
</slot> | ||
|
||
<slot name="info-text"> | ||
<p class="pb-4 pt-2 text-sm">{{ $t('account.recover.information') }}</p> | ||
</slot> | ||
|
||
<slot name="form"> | ||
<UiAutoForm | ||
class="space-y-6" | ||
:schema="schema" | ||
:field-config="{ | ||
email: { | ||
label: $t('account.email.label'), | ||
inputProps: { | ||
type: 'email', | ||
placeholder: $t('account.email.placeholder'), | ||
}, | ||
} | ||
}" | ||
@submit="recover" | ||
> | ||
<slot name="buttons"> | ||
<div class="flex flex-wrap gap-4"> | ||
<slot name="back-button"> | ||
<UiButton variant="outline" class="w-fit grow sm:grow-0"> | ||
<NuxtLinkLocale to="/account/login">{{ $t('account.recover.backButton') }}</NuxtLinkLocale> | ||
</UiButton> | ||
</slot> | ||
|
||
<slot name="submit-button"> | ||
<UiButton type="submit" :is-loading="isLoading" class="min-w-52 grow"> | ||
{{ $t('account.recover.submitButton') }} | ||
</UiButton> | ||
</slot> | ||
</div> | ||
</slot> | ||
</UiAutoForm> | ||
</slot> | ||
</div> | ||
</slot> | ||
</template> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider adding error handling for failed recovery attempts.
While the component handles success messages well, there's no visible mechanism to display errors if the recovery process fails. Consider adding error handling and displaying appropriate error messages.
You could add an additional prop like errorMessage
and display it conditionally:
<script setup lang="ts">
import * as z from 'zod';
const { t } = useI18n();
withDefaults(
defineProps<{
isLoading?: boolean;
showSuccessMessage?: boolean;
+ errorMessage?: string;
}>(),
{
isLoading: false,
showSuccessMessage: false,
+ errorMessage: '',
},
);
And in the template:
<div v-auto-animate>
<slot name="success-message">
<template v-if="showSuccessMessage">
<UiAlert variant="successful" class="mb-4 flex gap-4">
<!-- ... existing success message ... -->
</UiAlert>
</template>
</slot>
+ <slot name="error-message">
+ <template v-if="errorMessage">
+ <UiAlert variant="destructive" class="mb-4 flex gap-4">
+ <slot name="error-alert-icon">
+ <Icon name="mdi:alert-circle" class="size-4 shrink-0" />
+ </slot>
+ <div>
+ <UiAlertTitle>{{ $t('account.recover.errorHeader') }}</UiAlertTitle>
+ <UiAlertDescription>
+ {{ errorMessage }}
+ </UiAlertDescription>
+ </div>
+ </UiAlert>
+ </template>
+ </slot>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<script setup lang="ts"> | |
import * as z from 'zod'; | |
const { t } = useI18n(); | |
withDefaults( | |
defineProps<{ | |
isLoading?: boolean; | |
showSuccessMessage?: boolean; | |
}>(), | |
{ | |
isLoading: false, | |
showSuccessMessage: false, | |
}, | |
); | |
const emits = defineEmits<{ | |
recover: [recoverData: RecoverData]; | |
}>(); | |
const schema = z.object({ | |
email: z | |
.string({ | |
required_error: t('account.email.error'), | |
}) | |
.email(), | |
}); | |
export type RecoverData = z.infer<typeof schema>; | |
const recover = (recoverData: RecoverData) => { | |
emits('recover', recoverData); | |
}; | |
</script> | |
<template> | |
<slot name="wrapper"> | |
<div v-auto-animate> | |
<slot name="success-message"> | |
<template v-if="showSuccessMessage"> | |
<UiAlert variant="successful" class="mb-4 flex gap-4"> | |
<slot name="alert-icon"> | |
<Icon name="mdi:check" class="size-4 shrink-0" /> | |
</slot> | |
<div> | |
<UiAlertTitle>{{ $t('account.recover.successHeader') }}</UiAlertTitle> | |
<UiAlertDescription> | |
{{ $t('account.recover.successMessage') }} | |
</UiAlertDescription> | |
</div> | |
</UiAlert> | |
</template> | |
</slot> | |
<slot name="header"> | |
<h1 class="text-lg font-semibold">{{ $t('account.recover.header') }}</h1> | |
<hr> | |
</slot> | |
<slot name="info-text"> | |
<p class="pb-4 pt-2 text-sm">{{ $t('account.recover.information') }}</p> | |
</slot> | |
<slot name="form"> | |
<UiAutoForm | |
class="space-y-6" | |
:schema="schema" | |
:field-config="{ | |
email: { | |
label: $t('account.email.label'), | |
inputProps: { | |
type: 'email', | |
placeholder: $t('account.email.placeholder'), | |
}, | |
} | |
}" | |
@submit="recover" | |
> | |
<slot name="buttons"> | |
<div class="flex flex-wrap gap-4"> | |
<slot name="back-button"> | |
<UiButton variant="outline" class="w-fit grow sm:grow-0"> | |
<NuxtLinkLocale to="/account/login">{{ $t('account.recover.backButton') }}</NuxtLinkLocale> | |
</UiButton> | |
</slot> | |
<slot name="submit-button"> | |
<UiButton type="submit" :is-loading="isLoading" class="min-w-52 grow"> | |
{{ $t('account.recover.submitButton') }} | |
</UiButton> | |
</slot> | |
</div> | |
</slot> | |
</UiAutoForm> | |
</slot> | |
</div> | |
</slot> | |
</template> | |
<script setup lang="ts"> | |
import * as z from 'zod'; | |
const { t } = useI18n(); | |
withDefaults( | |
defineProps<{ | |
isLoading?: boolean; | |
showSuccessMessage?: boolean; | |
errorMessage?: string; | |
}>(), | |
{ | |
isLoading: false, | |
showSuccessMessage: false, | |
errorMessage: '', | |
}, | |
); | |
const emits = defineEmits<{ | |
recover: [recoverData: RecoverData]; | |
}>(); | |
const schema = z.object({ | |
email: z | |
.string({ | |
required_error: t('account.email.error'), | |
}) | |
.email(), | |
}); | |
export type RecoverData = z.infer<typeof schema>; | |
const recover = (recoverData: RecoverData) => { | |
emits('recover', recoverData); | |
}; | |
</script> | |
<template> | |
<slot name="wrapper"> | |
<div v-auto-animate> | |
<slot name="success-message"> | |
<template v-if="showSuccessMessage"> | |
<UiAlert variant="successful" class="mb-4 flex gap-4"> | |
<!-- ... existing success message ... --> | |
</UiAlert> | |
</template> | |
</slot> | |
<slot name="error-message"> | |
<template v-if="errorMessage"> | |
<UiAlert variant="destructive" class="mb-4 flex gap-4"> | |
<slot name="error-alert-icon"> | |
<Icon name="mdi:alert-circle" class="size-4 shrink-0" /> | |
</slot> | |
<div> | |
<UiAlertTitle>{{ $t('account.recover.errorHeader') }}</UiAlertTitle> | |
<UiAlertDescription> | |
{{ errorMessage }} | |
</UiAlertDescription> | |
</div> | |
</UiAlert> | |
</template> | |
</slot> | |
<slot name="header"> | |
<h1 class="text-lg font-semibold">{{ $t('account.recover.header') }}</h1> | |
<hr> | |
</slot> | |
<!-- rest of template unchanged --> | |
</div> | |
</slot> | |
</template> |
🤖 Prompt for AI Agents
In components/account/AccountRecoverInner.vue lines 1 to 98, the component lacks
error handling for failed recovery attempts. Add a new optional prop called
errorMessage of type string to the defineProps with a default of an empty
string. Then, in the template, conditionally render an error alert component
(similar to the success message) that displays the errorMessage when it is not
empty. This will provide users with visible feedback when recovery fails.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Style
Localization