diff --git a/web/src/components/features/auth/LoginForm.tsx b/web/src/components/features/auth/LoginForm.tsx
index d01e88e70..72808bffa 100644
--- a/web/src/components/features/auth/LoginForm.tsx
+++ b/web/src/components/features/auth/LoginForm.tsx
@@ -287,26 +287,32 @@ const LoginOptions: React.FC<{
autoComplete="new-password"
tabIndex={5}
/>
- {signUpFlow && (
-
- )}
{signUpFlow && (
- {
- setPassword2(v.target.value);
- }}
- disabled={!!loading}
- id="password2"
- type="password"
- name="password2"
- placeholder="Confirm password"
- autoComplete="new-password"
- tabIndex={6}
- />
+ <>
+ {
+ setPassword2(v.target.value);
+ }}
+ disabled={!!loading}
+ id="password2"
+ type="password"
+ name="password2"
+ placeholder="Confirm password"
+ autoComplete="new-password"
+ tabIndex={6}
+ />
+
+ {password && (
+
+ )}
+ >
)}
@@ -315,21 +321,16 @@ const LoginOptions: React.FC<{
))}
-
-
-
+
diff --git a/web/src/components/features/auth/PolicyCheck.stories.tsx b/web/src/components/features/auth/PolicyCheck.stories.tsx
deleted file mode 100644
index 4676ee21c..000000000
--- a/web/src/components/features/auth/PolicyCheck.stories.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { PolicyCheck } from './PolicyCheck';
-import { fn } from '@storybook/test';
-
-const meta = {
- title: 'Features/Auth/PolicyCheck',
- component: PolicyCheck,
- parameters: {
- layout: 'centered'
- },
- tags: ['autodocs'],
- argTypes: {
- password: { control: 'text' },
- show: { control: 'boolean' },
- placement: {
- control: 'select',
- options: ['top', 'right', 'bottom', 'left']
- },
- onCheckChange: { action: 'onCheckChange' }
- }
-} satisfies Meta
;
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- args: {
- password: '',
- show: true,
- placement: 'left',
- onCheckChange: fn()
- }
-};
-
-export const ValidPassword: Story = {
- args: {
- password: 'Test123!@#',
- show: true,
- placement: 'left',
- onCheckChange: fn()
- }
-};
-
-export const InvalidPassword: Story = {
- args: {
- password: 'weak',
- show: true,
- placement: 'left',
- onCheckChange: fn()
- }
-};
-
-export const DifferentPlacement: Story = {
- args: {
- password: 'Test123!@#',
- show: true,
- placement: 'right',
- onCheckChange: fn()
- }
-};
-
-export const WithCustomChildren: Story = {
- args: {
- password: 'Test123!@#',
- show: true,
- placement: 'left',
- onCheckChange: fn(),
- children: Custom trigger element
- }
-};
diff --git a/web/src/components/features/auth/PolicyCheck.tsx b/web/src/components/features/auth/PolicyCheck.tsx
index 86ebfb26a..4511265b5 100644
--- a/web/src/components/features/auth/PolicyCheck.tsx
+++ b/web/src/components/features/auth/PolicyCheck.tsx
@@ -3,14 +3,34 @@ import React, { useEffect, useMemo } from 'react';
import { Text } from '@/components/ui/typography';
import { Popover, PopoverProps } from '@/components/ui/popover/Popover';
import { Button } from '@/components/ui/buttons/Button';
+import { validate } from 'email-validator';
+
+const PasswordCheckItem: React.FC<{
+ passwordGood: boolean;
+ text: string;
+}> = ({ passwordGood, text }) => {
+ return (
+
+ {passwordGood ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
{text}
+
+ );
+};
export const PolicyCheck: React.FC<{
+ email: string;
password: string;
- show: boolean;
- onCheckChange?: (value: boolean) => void;
- children?: React.ReactNode;
- placement?: 'top' | 'right' | 'bottom' | 'left';
-}> = ({ password, show, onCheckChange, children, placement = 'left' }) => {
+ password2: string | undefined;
+ onChangePolicyCheck: (passed: boolean) => void;
+}> = ({ email, password, password2, onChangePolicyCheck }) => {
const items = useMemo(() => {
const containsNumber = /\d/;
const containsSpecialChar = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/;
@@ -32,6 +52,10 @@ export const PolicyCheck: React.FC<{
};
const items = [
+ {
+ text: 'Email is valid',
+ check: validate(email)
+ },
{
text: 'Contains a number',
check: passwordGood.numberCheck
@@ -51,90 +75,39 @@ export const PolicyCheck: React.FC<{
{
text: 'Is at least 8 characters long',
check: passwordGood.passwordLengthCheck
+ },
+ {
+ text: 'Passwords match',
+ check: password === password2 || password2 === undefined
}
];
return items;
- }, [password]);
+ }, [password, email, password2]);
- const allCompleted = useMemo(() => {
- return items.every((item) => item.check);
+ const percentageCompleted = useMemo(() => {
+ const numberOfChecks = items.length;
+ const numberOfChecksCompleted = items.filter((item) => item.check).length;
+ return (numberOfChecksCompleted / numberOfChecks) * 100;
}, [items]);
useEffect(() => {
- if (show && onCheckChange) {
- onCheckChange(allCompleted);
- }
- }, [show, allCompleted, onCheckChange]);
-
- const PasswordCheck: React.FC<{
- passwordGood: boolean;
- text: string;
- }> = ({ passwordGood, text }) => {
- return (
-
- {passwordGood ? (
-
-
-
- ) : (
-
-
-
- )}
-
{text}
-
- );
- };
-
- const sideMemo: PopoverProps['side'] = useMemo(() => {
- switch (placement) {
- case 'top':
- return 'top';
- case 'right':
- return 'right';
- case 'bottom':
- return 'bottom';
- case 'left':
- return 'left';
- }
- }, [placement]);
-
- const alignMemo: PopoverProps['align'] = useMemo(() => {
- switch (placement) {
- case 'top':
- return 'start';
- case 'right':
- return 'end';
- case 'bottom':
- return 'start';
- case 'left':
- return 'end';
- }
- }, [placement]);
-
- if (!show) return children;
+ onChangePolicyCheck(percentageCompleted === 100);
+ }, [percentageCompleted, onChangePolicyCheck]);
return (
-
- {items.map((item, index) => (
-
- ))}
-
- }>
- {!children ? (
- : }>
- ) : (
- children
- )}
-
+
+
+
+ {items.map((item, index) => (
+
+ ))}
+
+
);
};
diff --git a/web/src/components/features/auth/ResetPasswordForm.tsx b/web/src/components/features/auth/ResetPasswordForm.tsx
index 370c8cdb8..bb82dba77 100644
--- a/web/src/components/features/auth/ResetPasswordForm.tsx
+++ b/web/src/components/features/auth/ResetPasswordForm.tsx
@@ -106,21 +106,22 @@ export const ResetPasswordForm: React.FC<{
/>
{
+ password2={password2}
+ email={email || ''}
+ onChangePolicyCheck={(v) => {
setGoodPassword(v);
- }}>
-
-
+ }}
+ />
+
+
>
)}