Skip to content
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

feat(features): use stepper for congregation creation workflow #2900

Merged
merged 2 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions converter/svg/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,12 @@ for await (const svgFile of svgFiles) {
);

if (componentName === 'IconLoading') {
data.replace(' animation,', ' animation: rotate 2s linear infinite,');
data = data.replace(
' animation,',
' animation: "rotate 2s linear infinite",'
);
} else {
data.replace(' animation,', '');
data = data.replace(' animation,', '');
}

data = data.replace('${iconName}', originalName);
Expand Down
9 changes: 9 additions & 0 deletions converter/svg/sources/name=congregation-access.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/RootWrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ const theme = createTheme({
span: {
fontFamily: `${font} !important`,
},
text: {
fontFamily: `${font} !important`,
},
},
},
},
Expand Down
56 changes: 56 additions & 0 deletions src/components/icons/IconCongregationAccess.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { SvgIcon, SxProps, Theme } from '@mui/material';

type IconProps = {
color?: string;
width?: number;
height?: number;
sx?: SxProps<Theme>;
className?: string;
};

const IconCongregationAccess = ({
color = '#222222',
width = 24,
height = 24,
sx = {},
className,
}: IconProps) => {
return (
<SvgIcon
className={`organized-icon-congregation-access ${className}`}
sx={{ width: `${width}px`, height: `${height}px`, ...sx }}
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<mask
id="mask0_14912_258760"
style={{ maskType: 'alpha' }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_14912_258760)">
<path
d="M4 20.8941V9.6442L11.5 4L19 9.6442V11.7038H17.5V10.3942L11.5 5.87495L5.49998 10.3942V19.3942H7.69038C7.69038 19.672 7.69038 19.8787 7.69038 20.1364C7.69038 20.3942 7.69038 20.5998 7.69038 20.8941H4Z"
fill={color}
/>
<path
d="M10.5298 20.8008V19.3145H21.4702V20.8008H10.5298ZM11.2215 17.3124L10.4278 16.8284L10.8558 16.0266H10V15.0585H10.8558L10.4278 14.2848L11.2215 13.8008L11.6493 14.5824L12.0772 13.8008L12.8632 14.2848L12.4354 15.0585H13.2911V16.0266H12.4354L12.8632 16.8284L12.0772 17.3124L11.6493 16.5028L11.2215 17.3124ZM15.5722 17.3124L14.7785 16.8284L15.2065 16.0266H14.3507V15.0585H15.2065L14.7785 14.2848L15.5722 13.8008L16 14.5824L16.4278 13.8008L17.2215 14.2848L16.7935 15.0585H17.6493V16.0266H16.7935L17.2215 16.8284L16.4278 17.3124L16 16.5028L15.5722 17.3124ZM19.9228 17.3124L19.1368 16.8284L19.5646 16.0266H18.7089V15.0585H19.5646L19.1368 14.2848L19.9228 13.8008L20.3507 14.5824L20.7785 13.8008L21.5722 14.2848L21.1442 15.0585H22V16.0266H21.1442L21.5722 16.8284L20.7785 17.3124L20.3507 16.5028L19.9228 17.3124Z"
fill={color}
/>
</g>
</svg>
</SvgIcon>
);
};

export default IconCongregationAccess;
3 changes: 2 additions & 1 deletion src/components/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export { default as IconCompassOn } from './IconCompassOn';
export { default as IconComputerVideo } from './IconComputerVideo';
export { default as IconComputer } from './IconComputer';
export { default as IconConference } from './IconConference';
export { default as IconCongregationAccess } from './IconCongregationAccess';
export { default as IconCongregation } from './IconCongregation';
export { default as IconContactUs } from './IconContactUs';
export { default as IconContact } from './IconContact';
Expand Down Expand Up @@ -186,8 +187,8 @@ export { default as IconPause } from './IconPause';
export { default as IconPermissionsPending } from './IconPermissionsPending';
export { default as IconPersonSearch } from './IconPersonSearch';
export { default as IconPerson } from './IconPerson';
export { default as IconPersonPlaceholder } from './IconPersonPlaceholder';
export { default as IconPersonalDay } from './IconPersonalDay';
export { default as IconPersonPlaceholder } from './IconPersonPlaceholder';
export { default as IconPhone } from './IconPhone';
export { default as IconPinCode } from './IconPinCode';
export { default as IconPin } from './IconPin';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { setIsAccountChoose } from '@services/recoil/app';
import { dbAppSettingsUpdate } from '@services/dexie/settings';
import { useSetRecoilState } from 'recoil';
import { isAccountChooseState, isUserAccountCreatedState } from '@states/app';

const useAccountChooser = () => {
const setIsAccountChoose = useSetRecoilState(isAccountChooseState);
const setIsUserAccountCreated = useSetRecoilState(isUserAccountCreatedState);
Comment on lines +2 to +7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Migration to useSetRecoilState is incomplete - Found legacy imports

The codebase still contains direct imports of setIsAccountChoose from the old location @services/recoil/app in:

  • src/features/app_start/shared/startup/useStartup.tsx
  • src/features/app_start/vip/signin/useSignin.tsx

These files need to be updated to use useSetRecoilState(isAccountChooseState) for consistency with the new pattern, as already implemented in other components like useTermsUse.tsx and useAccountChooser.tsx.

🔗 Analysis chain

LGTM! Verify state management changes across the application.

The switch to using useSetRecoilState hook directly is a good practice. However, let's verify that all components consuming these states are properly updated.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all components are properly using the new state atoms
# and there are no direct imports of the old setters

# Check for any remaining direct imports of setIsAccountChoose
rg "import.*setIsAccountChoose.*from.*@services/recoil/app"

# Check for usage of the new states to ensure consistent implementation
rg "isUserAccountCreatedState|isAccountChooseState" --type ts --type tsx

Length of output: 383


Script:

#!/bin/bash
# Let's try a more comprehensive search with proper file type handling

# Check for any usage of the state atoms
rg "isUserAccountCreatedState|isAccountChooseState" -t typescript -t tsx

# Check for components using useSetRecoilState with these atoms
rg "useSetRecoilState\(.*(?:isAccountChooseState|isUserAccountCreatedState)" -t typescript -t tsx

# Check for direct state setter imports from the old path
rg "import.*setIsAccountChoose.*from.*@services" -t typescript -t tsx

# Check the state definitions to understand the migration
rg "(?:isAccountChooseState|isUserAccountCreatedState).*=.*atom" -t typescript -t tsx

Length of output: 474


Script:

#!/bin/bash
# Search without explicit file type flags since rg's type detection seems unavailable

# Check for any usage of the state atoms
rg "isUserAccountCreatedState|isAccountChooseState"

# Check for components using useSetRecoilState with these atoms
rg "useSetRecoilState\(.*(?:isAccountChooseState|isUserAccountCreatedState)"

# Check for direct state setter imports from the old path
rg "import.*setIsAccountChoose"

# Check the state definitions
rg "(?:isAccountChooseState|isUserAccountCreatedState).*=.*atom"

Length of output: 3098


const handleChoosePocket = async () => {
await dbAppSettingsUpdate({ 'user_settings.account_type': 'pocket' });
await setIsAccountChoose(false);
setIsUserAccountCreated(false);
setIsAccountChoose(false);
};

const handleChooseVIP = async () => {
await dbAppSettingsUpdate({ 'user_settings.account_type': 'vip' });
await setIsAccountChoose(false);
setIsUserAccountCreated(false);
setIsAccountChoose(false);
};

return { handleChoosePocket, handleChooseVIP };
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { Box } from '@mui/material';
import { IconCongregationAccess, IconError, IconLoading } from '@icons/index';
import { useAppTranslation } from '@hooks/index';
import useCongregationAccessCode from './useCongregationAccessCode';
import Button from '@components/button';
import Criteria from './criteria';
import InfoMessage from '@components/info-message';
import Markup from '@components/text_markup';
import TextField from '@components/textfield';
import Typography from '@components/typography';
import VipInfoTip from '@features/app_start/vip/vip_info_tip';

const CongregationAccessCode = () => {
const { t } = useAppTranslation();

const {
isLengthPassed,
isNumberPassed,
isLowerCasePassed,
isUpperCasePassed,
isSpecialSymbolPassed,
isProcessing,
hideMessage,
message,
title,
variant,
isMatch,
setTmpAccessCode,
setTmpAccessCodeVerify,
tmpAccessCode,
tmpAccessCodeVerify,
btnActionDisabled,
handleSetAccessCode,
} = useCongregationAccessCode();

return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
width: '100%',
gap: '24px',
flexGrow: 1,
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
height: '100%',
width: '100%',
}}
>
<Markup
content={t('tr_createAccessCodeDesc')}
className="body-regular"
color="var(--grey-400)"
style={{ marginBottom: '24px' }}
/>

<Box
sx={{
display: 'flex',
padding: '8px 16px',
alignItems: 'center',
gap: '8px',
borderRadius: 'var(--radius-l)',
background: 'var(--orange-secondary)',
marginBottom: '32px',
}}
>
<IconError color="var(--orange-dark)" />
<Typography className="body-regular" color="var(--orange-dark)">
{t('tr_congregationAccessCodeNotice')}
</Typography>
</Box>

<Box
sx={{
display: 'flex',
flexDirection: 'column',
gap: '24px',
alignItems: 'flex-start',
alignSelf: 'stretch',
width: '100%',
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
gap: '16px',
width: '100%',
}}
>
<TextField
type="password"
label={t('tr_congregationAccessCodeCreate')}
variant="outlined"
autoComplete="off"
value={tmpAccessCode}
onChange={(e) => setTmpAccessCode(e.target.value)}
startIcon={<IconCongregationAccess />}
resetHelperPadding={true}
/>
<TextField
type="password"
label={t('tr_congregationAccessCodeVerify')}
variant="outlined"
autoComplete="off"
value={tmpAccessCodeVerify}
onChange={(e) => setTmpAccessCodeVerify(e.target.value)}
startIcon={<IconCongregationAccess />}
resetHelperPadding={true}
helperText={
Comment on lines +97 to +116
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance password field security and user experience.

Consider the following improvements:

  1. Add autocomplete="new-password" to prevent unwanted password suggestions
  2. Add password visibility toggle for better user experience
 <TextField
   type="password"
   label={t('tr_congregationAccessCodeCreate')}
   variant="outlined"
-  autoComplete="off"
+  autoComplete="new-password"
   value={tmpAccessCode}
   onChange={(e) => setTmpAccessCode(e.target.value)}
   startIcon={<IconCongregationAccess />}
+  endIcon={<IconPasswordVisibility onClick={togglePasswordVisibility} />}
   resetHelperPadding={true}
 />

Committable suggestion skipped: line range outside the PR's diff.

<Box
sx={{
padding: '8px 0px 0px 16px',
display: 'flex',
flexDirection: 'column',
gap: '4px',
}}
>
<Criteria
criteria={t('tr_congregationAccessCodeNoticeLength')}
passed={isLengthPassed}
/>
<Criteria
criteria={t('tr_encryptionCodeNoticeNumber')}
passed={isNumberPassed}
/>
<Criteria
criteria={t('tr_encryptionCodeNoticeLowerCase')}
passed={isLowerCasePassed}
/>
<Criteria
criteria={t('tr_encryptionCodeNoticeUpperCase')}
passed={isUpperCasePassed}
/>
<Criteria
criteria={t('tr_encryptionCodeNoticeSpecialSymbol')}
passed={isSpecialSymbolPassed}
/>
<Criteria
criteria={t('tr_encryptionCodeNoticeMatch')}
passed={isMatch}
/>
</Box>
}
/>
</Box>

<Button
variant="main"
sx={{ width: '100%' }}
onClick={handleSetAccessCode}
startIcon={
isProcessing ? <IconLoading width={22} height={22} /> : null
}
disabled={btnActionDisabled}
>
{t('tr_congregationAccessCodeSet')}
</Button>
</Box>
</Box>

<Box>
<VipInfoTip variant="congregationCodes" />

<Box id="onboarding-error" sx={{ display: 'none' }}>
<InfoMessage
variant={variant}
messageIcon={<IconError />}
messageHeader={title}
message={message}
onClose={hideMessage}
/>
</Box>
Comment on lines +171 to +179
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using React state for error message visibility.

Using display: 'none' suggests DOM manipulation. Consider using React state to manage visibility instead.

-<Box id="onboarding-error" sx={{ display: 'none' }}>
+<Box sx={{ display: message ? 'block' : 'none' }}>
   <InfoMessage
     variant={variant}
     messageIcon={<IconError />}
     messageHeader={title}
     message={message}
     onClose={hideMessage}
   />
 </Box>
📝 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
<Box id="onboarding-error" sx={{ display: 'none' }}>
<InfoMessage
variant={variant}
messageIcon={<IconError />}
messageHeader={title}
message={message}
onClose={hideMessage}
/>
</Box>
<Box sx={{ display: message ? 'block' : 'none' }}>
<InfoMessage
variant={variant}
messageIcon={<IconError />}
messageHeader={title}
message={message}
onClose={hideMessage}
/>
</Box>

</Box>
</Box>
);
};

export default CongregationAccessCode;
Loading