Skip to content

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

Open
wants to merge 37 commits into
base: release/v0.2
Choose a base branch
from

Conversation

MorennMcFly
Copy link
Contributor

@MorennMcFly MorennMcFly commented Apr 16, 2025

Summary by CodeRabbit

  • New Features

    • Introduced an account recovery page and form for password reset via email, including customizable success messages and loading states.
    • Added a new "successful" alert variant for visual feedback on successful actions.
  • Bug Fixes

    • Updated the forgotten password link to direct users to the correct recovery page.
  • Documentation

    • Added documentation detailing the account recovery feature, its usage, and customization options.
  • Style

    • Improved alert component styling for better consistency and added a new success alert example.
  • Localization

    • Updated and expanded localization files to support account recovery and improved translation key organization.

@MorennMcFly MorennMcFly self-assigned this Apr 16, 2025
@MorennMcFly MorennMcFly marked this pull request as draft April 16, 2025 12:23
@MorennMcFly MorennMcFly marked this pull request as ready for review April 16, 2025 12:37
Copy link

coderabbitai bot commented May 8, 2025

Walkthrough

This 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

File(s) Change Summary
components/account/AccountLoginInner.vue, i18n/locales/en-GB/account.json, i18n/locales/de-DE/account.json Refactored translation keys for login form fields and errors by removing the "login" segment; added/updated localization entries for account recovery.
components/account/AccountRecover.vue, components/account/AccountRecoverInner.vue, pages/account/recover.vue Added new Vue components and page for account recovery, including form, logic, and UI with slot customization and localized validation.
components/ui/alert/index.ts, stories/Alert.story.vue Added a new "successful" alert variant with green styling; updated alert story to demonstrate the new variant and adjusted icon styling.
docs/features/account-management.md Added documentation detailing the account recovery feature, component structure, props, events, slots, and usage example.

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
Loading

Poem

🐇✨
A hop and a skip, your password's amiss—
Now with recovery, you won't go amiss!
New forms and green alerts,
With translations that fit,
Success messages flutter,
As docs brightly glitter,
This bunny’s quite proud of this feature-filled hit!

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit 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.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit 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 Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

…ssword

# Conflicts:
#	i18n/locales/de-DE/account.json
#	i18n/locales/en-GB/account.json
@jkniest
Copy link
Member

jkniest commented May 20, 2025

@coderabbitai review

Copy link

coderabbitai bot commented May 20, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a 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 changes
  • aria-labelledby to connect form elements with their labels
  • role="status" to the success message

Example 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

📥 Commits

Reviewing files that changed from the base of the PR and between c101852 and ad10e7c.

📒 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 variant

Adding 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 styling

Adding 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 fixes

Adding 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 demo

The 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 fields

Moving 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 recovery

The 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 keys

The 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 keys

All 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 link

The 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.

Comment on lines +1 to +98
<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>
Copy link

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.

Suggested change
<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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants