From 7b6244aa754ef1ae7fd2393265da6ee059b722fc Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Thu, 5 Mar 2026 17:28:18 +0100 Subject: [PATCH 1/7] feat: add phone number display and edit to account profile (#984) Show phone number in the account profile section with an inline edit button that opens the phone edit overlay directly. --- src/screens/account.screen.tsx | 61 ++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/src/screens/account.screen.tsx b/src/screens/account.screen.tsx index a7383ac1..fe9708f0 100644 --- a/src/screens/account.screen.tsx +++ b/src/screens/account.screen.tsx @@ -7,6 +7,7 @@ import { UserAddress, UserProfile, Utils, + Validations, useApi, useSessionContext, useTransaction, @@ -28,12 +29,14 @@ import { StyledDropdown, StyledInput, StyledLoadingSpinner, + StyledIconButton, StyledVerticalStack, } from '@dfx.swiss/react-components'; import copy from 'copy-to-clipboard'; import { useEffect, useState } from 'react'; import { useForm, useWatch } from 'react-hook-form'; import { RecommendationsSection } from 'src/components/account/recommendations-section'; +import { EditOverlay } from 'src/components/overlay/edit-overlay'; import { KycStatus } from 'src/components/kyc-status'; import { Modal } from 'src/components/modal'; import { addressLabel } from 'src/config/labels'; @@ -80,7 +83,7 @@ export default function AccountScreen(): JSX.Element { const { getDetailTransactions, getUnassignedTransactions } = useTransaction(); const { navigate } = useNavigation(); const { isLoggedIn } = useSessionContext(); - const { user, isUserLoading, userAddresses } = useUserContext(); + const { user, isUserLoading, userAddresses, updatePhone } = useUserContext(); const { getRef, getProfile } = useUser(); const { width } = useWindowContext(); const { canClose, isEmbedded } = useAppHandlingContext(); @@ -95,6 +98,7 @@ export default function AccountScreen(): JSX.Element { const [isPdfLoading, setIsPdfLoading] = useState(false); const [pdfError, setPdfError] = useState(); const [showRecommendationModal, setShowRecommendationModal] = useState(false); + const [showPhoneModal, setShowPhoneModal] = useState(false); const [isDataLoading, setIsDataLoading] = useState(true); const [pdfBlockchains, setPdfBlockchains] = useState([]); @@ -296,19 +300,23 @@ export default function AccountScreen(): JSX.Element { const totalVolumeSum = totalVolumeItems?.reduce((acc, item) => acc + item.value, 0); const annualVolumeSum = annualVolumeItems?.reduce((acc, item) => acc + item.value, 0); - const title = showPdfModal - ? translate('screens/home', 'PDF Download Address Report') - : showRecommendationModal - ? translate('screens/recommendation', 'Create Invitation') - : isEmbedded - ? translate('screens/home', 'DFX services') - : translate('screens/home', 'Account'); - const hasBackButton = (canClose && !isEmbedded) || showPdfModal || showRecommendationModal; - const onBack = showPdfModal - ? closePdfModal - : showRecommendationModal - ? () => setShowRecommendationModal(false) - : undefined; + const title = showPhoneModal + ? translate('screens/settings', 'Edit phone number') + : showPdfModal + ? translate('screens/home', 'PDF Download Address Report') + : showRecommendationModal + ? translate('screens/recommendation', 'Create Invitation') + : isEmbedded + ? translate('screens/home', 'DFX services') + : translate('screens/home', 'Account'); + const hasBackButton = (canClose && !isEmbedded) || showPdfModal || showRecommendationModal || showPhoneModal; + const onBack = showPhoneModal + ? () => setShowPhoneModal(false) + : showPdfModal + ? closePdfModal + : showRecommendationModal + ? () => setShowRecommendationModal(false) + : undefined; const image = 'https://dfx.swiss/images/app/berge.jpg'; useLayoutOptions({ title, backButton: hasBackButton, onBack }); @@ -342,6 +350,14 @@ export default function AccountScreen(): JSX.Element { {formatAddress(profile.address)} )} + {profile.phone && ( + +
+ {profile.phone} + setShowPhoneModal(true)} inline /> +
+
+ )} {profile.organizationName && ( {profile.organizationName} @@ -470,6 +486,23 @@ export default function AccountScreen(): JSX.Element { )} + {/* Edit Phone Modal */} + setShowPhoneModal(false)}> + setShowPhoneModal(false)} + onEdit={async (result) => { + await updatePhone(result); + await loadProfile(); + setShowPhoneModal(false); + }} + /> + + {/* PDF Download Modal */} From cd9eb4c8cb5509407f4403fa2dd163db06ba19eb Mon Sep 17 00:00:00 2001 From: David May <85513542+davidleomay@users.noreply.github.com> Date: Fri, 6 Mar 2026 11:10:15 +0100 Subject: [PATCH 2/7] feat: moved phone/mail edit to account (#987) --- e2e/user-flows.spec.ts | 2 +- src/App.tsx | 8 ++-- src/screens/account.screen.tsx | 23 ++++++---- src/screens/edit-mail.screen.tsx | 4 +- src/screens/settings.screen.tsx | 73 +------------------------------- 5 files changed, 22 insertions(+), 88 deletions(-) diff --git a/e2e/user-flows.spec.ts b/e2e/user-flows.spec.ts index 02b67491..fd6a4431 100644 --- a/e2e/user-flows.spec.ts +++ b/e2e/user-flows.spec.ts @@ -140,7 +140,7 @@ test.describe('Account & Settings', () => { }); test('should load mail settings page', async ({ page }) => { - await page.goto(`/settings/mail?session=${token}`); + await page.goto(`/account/mail?session=${token}`); await page.waitForLoadState('networkidle'); await expect(page.locator('body')).toBeVisible(); }); diff --git a/src/App.tsx b/src/App.tsx index dd977871..5dcd1e2d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -94,12 +94,12 @@ export const Routes = [ element: withSuspense(), }, { - path: 'settings', - element: withSuspense(), + path: 'account/mail', + element: withSuspense(), }, { - path: 'settings/mail', - element: withSuspense(), + path: 'settings', + element: withSuspense(), }, { path: 'login', diff --git a/src/screens/account.screen.tsx b/src/screens/account.screen.tsx index fe9708f0..92b41636 100644 --- a/src/screens/account.screen.tsx +++ b/src/screens/account.screen.tsx @@ -338,7 +338,20 @@ export default function AccountScreen(): JSX.Element { minWidth={false} > {profile.mail && ( - {profile.mail} + +
+ {profile.mail} + navigate('/account/mail', { setRedirect: true })} inline /> +
+
+ )} + {profile.phone && ( + +
+ {profile.phone} + setShowPhoneModal(true)} inline /> +
+
)} {(profile.firstName || profile.lastName) && ( @@ -350,14 +363,6 @@ export default function AccountScreen(): JSX.Element { {formatAddress(profile.address)} )} - {profile.phone && ( - -
- {profile.phone} - setShowPhoneModal(true)} inline /> -
-
- )} {profile.organizationName && ( {profile.organizationName} diff --git a/src/screens/edit-mail.screen.tsx b/src/screens/edit-mail.screen.tsx index cb46fba7..210c7535 100644 --- a/src/screens/edit-mail.screen.tsx +++ b/src/screens/edit-mail.screen.tsx @@ -67,7 +67,7 @@ export default function EditMailScreen(): JSX.Element { setIsSubmitting(true); verifyMail(data.token) - .then(() => navigate('/settings')) + .then(() => navigate('/account')) .catch((e: ApiError) => e.statusCode === 403 ? setTokenInvalid(true) @@ -112,7 +112,7 @@ export default function EditMailScreen(): JSX.Element { prefill={user?.mail} placeholder={translate('screens/kyc', 'Email address')} validation={Validations.Mail} - onCancel={() => navigate('/settings')} + onCancel={() => navigate('/account')} onEdit={onSubmit} /> ) : ( diff --git a/src/screens/settings.screen.tsx b/src/screens/settings.screen.tsx index 14976083..7648c4fa 100644 --- a/src/screens/settings.screen.tsx +++ b/src/screens/settings.screen.tsx @@ -7,20 +7,12 @@ import { UserAddress, useUserContext, Utils, - Validations, } from '@dfx.swiss/react'; import { - AlignContent, - DfxIcon, Form, - IconColor, - IconSize, - IconVariant, SpinnerSize, StyledButton, StyledButtonWidth, - StyledDataTable, - StyledDataTableRow, StyledDropdown, StyledLoadingSpinner, StyledVerticalStack, @@ -54,8 +46,6 @@ enum OverlayType { DELETE_ADDRESS, DELETE_ACCOUNT, RENAME_ADDRESS, - EDIT_EMAIL, - EDIT_PHONE, EDIT_BANK_ACCOUNT, ADD_BANK_ACCOUNT, DELETE_BANK_ACCOUNT, @@ -66,8 +56,6 @@ const OverlayHeader: { [key in OverlayType]: string } = { [OverlayType.DELETE_ADDRESS]: 'Delete address', [OverlayType.DELETE_ACCOUNT]: 'Delete account', [OverlayType.RENAME_ADDRESS]: 'Rename address', - [OverlayType.EDIT_EMAIL]: 'Edit email', - [OverlayType.EDIT_PHONE]: 'Edit phone number', [OverlayType.EDIT_BANK_ACCOUNT]: 'Edit bank account', [OverlayType.ADD_BANK_ACCOUNT]: 'Add bank account', [OverlayType.DELETE_BANK_ACCOUNT]: 'Delete bank account', @@ -115,10 +103,6 @@ export default function SettingsScreen(): JSX.Element { } }, [selectedCurrency]); - useEffect(() => { - if (overlayType === OverlayType.EDIT_EMAIL) navigate('/settings/mail', { setRedirect: true }); - }, [overlayType]); - function onCloseOverlay(): void { setOverlayType(OverlayType.NONE); setOverlayData(undefined); @@ -167,46 +151,6 @@ export default function SettingsScreen(): JSX.Element {
- {!!(user?.mail || user?.phone) && ( - -

- {translate('screens/kyc', 'Personal Information')} -

- - {user?.mail && ( - -
-
- {translate('screens/kyc', 'Email address')} -
-
{user?.mail}
-
-
- -
-
- )} - {user?.phone && ( - -
-
- {translate('screens/kyc', 'Phone number')} -
-
{user?.phone}
-
-
- -
-
- )} -
-
- )} - {isLoadingBankAccounts ? (
@@ -339,7 +283,7 @@ function SettingsOverlay({ type, data, onClose }: SettingsOverlayProps): JSX.Ele const { width } = useWindowContext(); const { translate } = useSettingsContext(); const { setWallet } = useWalletContext(); - const { deleteAddress, deleteAccount, renameAddress, updatePhone } = useUserContext(); + const { deleteAddress, deleteAccount, renameAddress } = useUserContext(); const { updateAccount } = useBankAccountContext(); switch (type) { @@ -401,21 +345,6 @@ function SettingsOverlay({ type, data, onClose }: SettingsOverlayProps): JSX.Ele }} /> ); - case OverlayType.EDIT_PHONE: - return ( - { - await updatePhone(result); - onClose(); - }} - /> - ); case OverlayType.EDIT_BANK_ACCOUNT: return ; case OverlayType.ADD_BANK_ACCOUNT: From 13875217715d175d5446f4f60bd39ff4b339860f Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Fri, 6 Mar 2026 11:35:54 +0100 Subject: [PATCH 3/7] chore: bump @dfx.swiss/react to 1.3.0-beta.243 (#986) --- package-lock.json | 16 ++++++++-------- package.json | 4 ++-- src/config/labels.ts | 6 ++++++ src/screens/support-issue.screen.tsx | 16 +++++++++++++--- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index a8ecfc9e..1a09278b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "1.0.4", "license": "MIT", "dependencies": { - "@dfx.swiss/react": "^1.3.0-beta.240", - "@dfx.swiss/react-components": "^1.3.0-beta.240", + "@dfx.swiss/react": "^1.3.0-beta.243", + "@dfx.swiss/react-components": "^1.3.0-beta.243", "@ledgerhq/hw-app-btc": "^6.24.1", "@ledgerhq/hw-app-eth": "^6.33.7", "@ledgerhq/hw-transport-webhid": "^6.27.19", @@ -2631,9 +2631,9 @@ } }, "node_modules/@dfx.swiss/react": { - "version": "1.3.0-beta.240", - "resolved": "https://registry.npmjs.org/@dfx.swiss/react/-/react-1.3.0-beta.240.tgz", - "integrity": "sha512-kS6daIyMDb8yu5XDY+ixVQDyxrAOBKVk+AchqUW4peLWSwRG8M9XkguZNsqXBWE/JE1oPhcl28iV8oxaINCM3w==", + "version": "1.3.0-beta.243", + "resolved": "https://registry.npmjs.org/@dfx.swiss/react/-/react-1.3.0-beta.243.tgz", + "integrity": "sha512-a8zdA6u1f5REzaVPZusrU4Nj73sNi1oxitcIbt2DG5wsaLl4Hi1jzOASD5hPi0PLE8yvxI4R3kU7pkyO3Q/OeA==", "license": "MIT", "dependencies": { "ibantools": "^4.2.1", @@ -2644,9 +2644,9 @@ } }, "node_modules/@dfx.swiss/react-components": { - "version": "1.3.0-beta.240", - "resolved": "https://registry.npmjs.org/@dfx.swiss/react-components/-/react-components-1.3.0-beta.240.tgz", - "integrity": "sha512-P8ACYD3vg0vU5SLoqPqyMnZgzcC1vhuLXJ6NTW83VHR91/Naf5IYusEcDmIHaXezWkEF7uXvYWltHVYtnWdeNw==", + "version": "1.3.0-beta.243", + "resolved": "https://registry.npmjs.org/@dfx.swiss/react-components/-/react-components-1.3.0-beta.243.tgz", + "integrity": "sha512-c+ifSfFLp0NMDl+xzX0krUXrSlMqHEflf76Pxgb1xmqXNYiPnkEJnjTb2qTb3nFmRxcn4D22MIAdDW8AEtwJRQ==", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.18.1", diff --git a/package.json b/package.json index 5524dd19..405b992b 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "access": "public" }, "dependencies": { - "@dfx.swiss/react": "^1.3.0-beta.240", - "@dfx.swiss/react-components": "^1.3.0-beta.240", + "@dfx.swiss/react": "^1.3.0-beta.243", + "@dfx.swiss/react-components": "^1.3.0-beta.243", "@ledgerhq/hw-app-btc": "^6.24.1", "@ledgerhq/hw-app-eth": "^6.33.7", "@ledgerhq/hw-transport-webhid": "^6.27.19", diff --git a/src/config/labels.ts b/src/config/labels.ts index 9b627df6..961d9a18 100644 --- a/src/config/labels.ts +++ b/src/config/labels.ts @@ -135,6 +135,7 @@ export const IssueTypeLabels = { [SupportIssueType.PARTNERSHIP_REQUEST]: 'Partnership request', [SupportIssueType.NOTIFICATION_OF_CHANGES]: 'Notification of changes', [SupportIssueType.BUG_REPORT]: 'Bug report', + [SupportIssueType.VERIFICATION_CALL]: 'Verification call', }; export const IssueReasonLabels = { @@ -142,6 +143,11 @@ export const IssueReasonLabels = { [SupportIssueReason.DATA_REQUEST]: 'Data request', [SupportIssueReason.FUNDS_NOT_RECEIVED]: 'Funds not received', [SupportIssueReason.TRANSACTION_MISSING]: 'Transaction missing', + [SupportIssueReason.REJECT_CALL]: 'Reject call', + [SupportIssueReason.REPEAT_CALL]: 'Repeat call', + [SupportIssueReason.NAME_CHANGED]: 'Name changed', + [SupportIssueReason.ADDRESS_CHANGED]: 'Address changed', + [SupportIssueReason.CIVIL_STATUS_CHANGED]: 'Civil status changed', }; export const FileTypeLabels = { diff --git a/src/screens/support-issue.screen.tsx b/src/screens/support-issue.screen.tsx index 9d601ae8..ea009685 100644 --- a/src/screens/support-issue.screen.tsx +++ b/src/screens/support-issue.screen.tsx @@ -59,8 +59,18 @@ const IssueReasons: { [t in SupportIssueType]: SupportIssueReason[] } = { [SupportIssueType.KYC_ISSUE]: [SupportIssueReason.OTHER], [SupportIssueType.LIMIT_REQUEST]: [SupportIssueReason.OTHER], [SupportIssueType.PARTNERSHIP_REQUEST]: [SupportIssueReason.OTHER], - [SupportIssueType.NOTIFICATION_OF_CHANGES]: [SupportIssueReason.OTHER], + [SupportIssueType.NOTIFICATION_OF_CHANGES]: [ + SupportIssueReason.NAME_CHANGED, + SupportIssueReason.ADDRESS_CHANGED, + SupportIssueReason.CIVIL_STATUS_CHANGED, + SupportIssueReason.OTHER, + ], [SupportIssueType.BUG_REPORT]: [SupportIssueReason.OTHER], + [SupportIssueType.VERIFICATION_CALL]: [ + SupportIssueReason.REJECT_CALL, + SupportIssueReason.REPEAT_CALL, + SupportIssueReason.OTHER, + ], }; interface FormData { @@ -398,8 +408,8 @@ export default function SupportIssueScreen(): JSX.Element { item === AddAccount ? translate('general/actions', item) : item === NoIban - ? translate('screens/iban', item) - : Utils.formatIban(item) ?? '', + ? translate('screens/iban', item) + : (Utils.formatIban(item) ?? ''), { displayLength: 30 }, ) } From 0f57820d590b0679bc96bd37bd51c65eb8bdd70b Mon Sep 17 00:00:00 2001 From: David May <85513542+davidleomay@users.noreply.github.com> Date: Fri, 6 Mar 2026 15:30:31 +0100 Subject: [PATCH 4/7] feat: allow anonymous TX display with order UID (#988) --- src/screens/transaction.screen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screens/transaction.screen.tsx b/src/screens/transaction.screen.tsx index 83273632..6720cd94 100644 --- a/src/screens/transaction.screen.tsx +++ b/src/screens/transaction.screen.tsx @@ -86,7 +86,7 @@ export default function TransactionScreen(): JSX.Element { const [isCsvLoading, setIsCsvLoading] = useState(); const [error, setError] = useState(); - const isTransaction = id && id.startsWith('T'); + const isTransaction = id && (id.startsWith('T') || id.startsWith('Q')); const isRefund = isTransaction && pathname.includes('/refund'); async function exportCsv(type: ExportType) { From b8f94f40175c23737a9357a77a765ad4789fb835 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 12 Mar 2026 10:37:25 +0100 Subject: [PATCH 5/7] feat: replace phone edit modal with KYC PhoneChange step (#990) --- package-lock.json | 16 +- package.json | 4 +- src/config/labels.ts | 2 + src/hooks/kyc-helper.hook.ts | 3 + src/screens/account.screen.tsx | 48 ++--- src/screens/kyc.screen.tsx | 331 +++++++++++++++++++++++++++++ src/translations/languages/de.json | 6 + src/translations/languages/fr.json | 6 + src/translations/languages/it.json | 6 + 9 files changed, 381 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a09278b..75243f29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "1.0.4", "license": "MIT", "dependencies": { - "@dfx.swiss/react": "^1.3.0-beta.243", - "@dfx.swiss/react-components": "^1.3.0-beta.243", + "@dfx.swiss/react": "^1.3.0-beta.247", + "@dfx.swiss/react-components": "^1.3.0-beta.247", "@ledgerhq/hw-app-btc": "^6.24.1", "@ledgerhq/hw-app-eth": "^6.33.7", "@ledgerhq/hw-transport-webhid": "^6.27.19", @@ -2631,9 +2631,9 @@ } }, "node_modules/@dfx.swiss/react": { - "version": "1.3.0-beta.243", - "resolved": "https://registry.npmjs.org/@dfx.swiss/react/-/react-1.3.0-beta.243.tgz", - "integrity": "sha512-a8zdA6u1f5REzaVPZusrU4Nj73sNi1oxitcIbt2DG5wsaLl4Hi1jzOASD5hPi0PLE8yvxI4R3kU7pkyO3Q/OeA==", + "version": "1.3.0-beta.247", + "resolved": "https://registry.npmjs.org/@dfx.swiss/react/-/react-1.3.0-beta.247.tgz", + "integrity": "sha512-tKQKYVoBCiqtNFG5CkGpai17fzMVyLss2JjsT7cGlFcjFIBph9u//WOJFceuZBXCny3vLi3RMxaji3p5zdQ0Pw==", "license": "MIT", "dependencies": { "ibantools": "^4.2.1", @@ -2644,9 +2644,9 @@ } }, "node_modules/@dfx.swiss/react-components": { - "version": "1.3.0-beta.243", - "resolved": "https://registry.npmjs.org/@dfx.swiss/react-components/-/react-components-1.3.0-beta.243.tgz", - "integrity": "sha512-c+ifSfFLp0NMDl+xzX0krUXrSlMqHEflf76Pxgb1xmqXNYiPnkEJnjTb2qTb3nFmRxcn4D22MIAdDW8AEtwJRQ==", + "version": "1.3.0-beta.247", + "resolved": "https://registry.npmjs.org/@dfx.swiss/react-components/-/react-components-1.3.0-beta.247.tgz", + "integrity": "sha512-P2rRi/b8qXWYHhPRfsu3/+7Xsc6feke2wqGCVch/UG7X8v0ThPwah8T0gNGi1AbrQiUXT//snImhCehoWPwunQ==", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.18.1", diff --git a/package.json b/package.json index 405b992b..7bb20034 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "access": "public" }, "dependencies": { - "@dfx.swiss/react": "^1.3.0-beta.243", - "@dfx.swiss/react-components": "^1.3.0-beta.243", + "@dfx.swiss/react": "^1.3.0-beta.247", + "@dfx.swiss/react-components": "^1.3.0-beta.247", "@ledgerhq/hw-app-btc": "^6.24.1", "@ledgerhq/hw-app-eth": "^6.33.7", "@ledgerhq/hw-transport-webhid": "^6.27.19", diff --git a/src/config/labels.ts b/src/config/labels.ts index 961d9a18..25e745cb 100644 --- a/src/config/labels.ts +++ b/src/config/labels.ts @@ -161,6 +161,8 @@ export const FileTypeLabels = { [FileType.RESIDENCE_PERMIT]: 'Residence permit', [FileType.ADDITIONAL_DOCUMENTS]: 'Additional documents', [FileType.AUTHORITY]: 'Power of Attorney', + [FileType.ADDRESS_CHANGE]: 'Address change', + [FileType.NAME_CHANGE]: 'Name change', }; // --- ADDRESSES --- // diff --git a/src/hooks/kyc-helper.hook.ts b/src/hooks/kyc-helper.hook.ts index 951cbe26..8bac1f50 100644 --- a/src/hooks/kyc-helper.hook.ts +++ b/src/hooks/kyc-helper.hook.ts @@ -79,6 +79,9 @@ export function useKycHelper(): KycHelperInterface { [KycStepName.DFX_APPROVAL]: 'DFX approval', [KycStepName.PAYMENT_AGREEMENT]: 'Assignment agreement', [KycStepName.RECALL_AGREEMENT]: 'Recall agreement', + [KycStepName.PHONE_CHANGE]: 'Phone number change', + [KycStepName.ADDRESS_CHANGE]: 'Address change', + [KycStepName.NAME_CHANGE]: 'Name change', }; const typeMap: Record = { diff --git a/src/screens/account.screen.tsx b/src/screens/account.screen.tsx index 92b41636..21deb361 100644 --- a/src/screens/account.screen.tsx +++ b/src/screens/account.screen.tsx @@ -2,12 +2,12 @@ import { ApiError, Blockchain, DetailTransaction, + KycStepName, PdfDocument, Referral, UserAddress, UserProfile, Utils, - Validations, useApi, useSessionContext, useTransaction, @@ -36,13 +36,13 @@ import copy from 'copy-to-clipboard'; import { useEffect, useState } from 'react'; import { useForm, useWatch } from 'react-hook-form'; import { RecommendationsSection } from 'src/components/account/recommendations-section'; -import { EditOverlay } from 'src/components/overlay/edit-overlay'; import { KycStatus } from 'src/components/kyc-status'; import { Modal } from 'src/components/modal'; import { addressLabel } from 'src/config/labels'; import { Urls } from 'src/config/urls'; import { useLayoutContext } from 'src/contexts/layout.context'; import { useWindowContext } from 'src/contexts/window.context'; +import { useKycHelper } from 'src/hooks/kyc-helper.hook'; import { useUserGuard } from 'src/hooks/guard.hook'; import { useLayoutOptions } from 'src/hooks/layout-config.hook'; import { useNavigation } from 'src/hooks/navigation.hook'; @@ -83,12 +83,13 @@ export default function AccountScreen(): JSX.Element { const { getDetailTransactions, getUnassignedTransactions } = useTransaction(); const { navigate } = useNavigation(); const { isLoggedIn } = useSessionContext(); - const { user, isUserLoading, userAddresses, updatePhone } = useUserContext(); + const { user, isUserLoading, userAddresses } = useUserContext(); const { getRef, getProfile } = useUser(); const { width } = useWindowContext(); const { canClose, isEmbedded } = useAppHandlingContext(); const { isInitialized, setWallet } = useWalletContext(); const { changeAddress } = useUserContext(); + const { startStep } = useKycHelper(); const { rootRef } = useLayoutContext(); const { call } = useApi(); const [transactions, setTransactions] = useState[]>(); @@ -98,7 +99,7 @@ export default function AccountScreen(): JSX.Element { const [isPdfLoading, setIsPdfLoading] = useState(false); const [pdfError, setPdfError] = useState(); const [showRecommendationModal, setShowRecommendationModal] = useState(false); - const [showPhoneModal, setShowPhoneModal] = useState(false); + const [isDataLoading, setIsDataLoading] = useState(true); const [pdfBlockchains, setPdfBlockchains] = useState([]); @@ -300,19 +301,15 @@ export default function AccountScreen(): JSX.Element { const totalVolumeSum = totalVolumeItems?.reduce((acc, item) => acc + item.value, 0); const annualVolumeSum = annualVolumeItems?.reduce((acc, item) => acc + item.value, 0); - const title = showPhoneModal - ? translate('screens/settings', 'Edit phone number') - : showPdfModal + const title = showPdfModal ? translate('screens/home', 'PDF Download Address Report') : showRecommendationModal ? translate('screens/recommendation', 'Create Invitation') : isEmbedded ? translate('screens/home', 'DFX services') : translate('screens/home', 'Account'); - const hasBackButton = (canClose && !isEmbedded) || showPdfModal || showRecommendationModal || showPhoneModal; - const onBack = showPhoneModal - ? () => setShowPhoneModal(false) - : showPdfModal + const hasBackButton = (canClose && !isEmbedded) || showPdfModal || showRecommendationModal; + const onBack = showPdfModal ? closePdfModal : showRecommendationModal ? () => setShowRecommendationModal(false) @@ -349,18 +346,24 @@ export default function AccountScreen(): JSX.Element {
{profile.phone} - setShowPhoneModal(true)} inline /> + startStep(KycStepName.PHONE_CHANGE)} inline />
)} {(profile.firstName || profile.lastName) && ( - {[profile.firstName, profile.lastName].filter(Boolean).join(' ')} +
+ {[profile.firstName, profile.lastName].filter(Boolean).join(' ')} + startStep(KycStepName.NAME_CHANGE)} inline /> +
)} {profile.address && ( - {formatAddress(profile.address)} +
+ {formatAddress(profile.address)} + startStep(KycStepName.ADDRESS_CHANGE)} inline /> +
)} {profile.organizationName && ( @@ -491,23 +494,6 @@ export default function AccountScreen(): JSX.Element {
)} - {/* Edit Phone Modal */} - setShowPhoneModal(false)}> - setShowPhoneModal(false)} - onEdit={async (result) => { - await updatePhone(result); - await loadProfile(); - setShowPhoneModal(false); - }} - /> - - {/* PDF Download Modal */} diff --git a/src/screens/kyc.screen.tsx b/src/screens/kyc.screen.tsx index 9394fe82..143728ac 100644 --- a/src/screens/kyc.screen.tsx +++ b/src/screens/kyc.screen.tsx @@ -7,7 +7,10 @@ import { GenderType, GoodsCategory, GoodsType, + KycAddress, KycBeneficialData, + KycChangeAddressData, + KycChangeNameData, KycContactData, KycFinancialOption, KycFinancialQuestion, @@ -521,6 +524,18 @@ function KycEdit(props: EditProps): JSX.Element { case KycStepName.RECALL_AGREEMENT: return ; + + case KycStepName.PHONE_CHANGE: + return ; + + case KycStepName.ADDRESS_CHANGE: + return ; + + case KycStepName.NAME_CHANGE: + return ; + + default: + return <>; } } @@ -2212,6 +2227,322 @@ function ManualIdent({ rootRef, code, step, onDone }: EditProps): JSX.Element { ); } +// --- Change Steps --- + +interface PhoneChangeFormData { + phone: string; +} + +function PhoneChangeData({ code, isLoading, step, onDone }: EditProps): JSX.Element { + const { translate, translateError } = useSettingsContext(); + const { setPhoneChangeData } = useKyc(); + + const [isUpdating, setIsUpdating] = useState(false); + const [error, setError] = useState(); + + const { + control, + handleSubmit, + formState: { isValid, errors }, + } = useForm({ mode: 'onTouched' }); + + function onSubmit(data: PhoneChangeFormData) { + if (!step.session) return; + + setIsUpdating(true); + setError(undefined); + setPhoneChangeData(code, step.session.url, { phone: data.phone }) + .then(onDone) + .catch((error: ApiError) => setError(error.message ?? 'Unknown error')) + .finally(() => setIsUpdating(false)); + } + + const rules = Utils.createRules({ + phone: [Validations.Required, Validations.Phone], + }); + + return ( +
+ + + + {error && ( +
+ +
+ )} + + +
+
+ ); +} + +interface AddressChangeFormData { + file: File; + address: KycAddress; +} + +function AddressChangeData({ rootRef, code, isLoading, step, onDone }: EditProps): JSX.Element { + const { allowedCountries, translate, translateError } = useSettingsContext(); + const { setAddressChangeData } = useKyc(); + const { countryCode } = useGeoLocation(); + + const [isUpdating, setIsUpdating] = useState(false); + const [error, setError] = useState(); + + const ipCountry = allowedCountries.find((c) => c.symbol === countryCode); + + const { + control, + handleSubmit, + formState: { isValid, errors }, + } = useForm({ mode: 'onTouched', defaultValues: { address: { country: ipCountry } } }); + + async function onSubmit(data: AddressChangeFormData) { + if (!step.session) return; + + const file = data.file && (await toBase64(data.file)); + if (!file) { + setError('No file selected'); + return; + } + + setIsUpdating(true); + setError(undefined); + setAddressChangeData(code, step.session.url, { + file, + fileName: data.file.name, + address: data.address, + }) + .then(onDone) + .catch((error: ApiError) => setError(error.message ?? 'Unknown error')) + .finally(() => setIsUpdating(false)); + } + + const rules = Utils.createRules({ + ['address.street']: Validations.Required, + ['address.zip']: Validations.Required, + ['address.city']: Validations.Required, + ['address.country']: Validations.Required, + file: [ + Validations.Required, + Validations.Custom((file) => (!file || DefaultFileTypes.includes(file.type) ? true : 'file_type')), + ], + }); + + return ( +
+ + +

+ {translate('screens/kyc', 'New address')} +

+ + + + + + + + + + rootRef={rootRef} + name="address.country" + autocomplete="country" + label={translate('screens/kyc', 'Country')} + placeholder={translate('general/actions', 'Select') + '...'} + items={allowedCountries} + labelFunc={(item) => item.name} + filterFunc={(i, s) => !s || [i.name, i.symbol].some((w) => w.toLowerCase().includes(s.toLowerCase()))} + matchFunc={(i, s) => i.name.toLowerCase() === s?.toLowerCase()} + smallLabel + /> +
+ + +

+ {translate('screens/kyc', 'Proof document')} +

+ +
+ + {error && ( +
+ +
+ )} + + +
+
+ ); +} + +interface NameChangeFormData { + file: File; + firstName: string; + lastName: string; +} + +function NameChangeData({ code, isLoading, step, onDone }: EditProps): JSX.Element { + const { translate, translateError } = useSettingsContext(); + const { setNameChangeData } = useKyc(); + + const [isUpdating, setIsUpdating] = useState(false); + const [error, setError] = useState(); + + const { + control, + handleSubmit, + formState: { isValid, errors }, + } = useForm({ mode: 'onTouched' }); + + async function onSubmit(data: NameChangeFormData) { + if (!step.session) return; + + const file = data.file && (await toBase64(data.file)); + if (!file) { + setError('No file selected'); + return; + } + + setIsUpdating(true); + setError(undefined); + setNameChangeData(code, step.session.url, { + file, + fileName: data.file.name, + firstName: data.firstName, + lastName: data.lastName, + }) + .then(onDone) + .catch((error: ApiError) => setError(error.message ?? 'Unknown error')) + .finally(() => setIsUpdating(false)); + } + + const rules = Utils.createRules({ + firstName: Validations.Required, + lastName: Validations.Required, + file: [ + Validations.Required, + Validations.Custom((file) => (!file || DefaultFileTypes.includes(file.type) ? true : 'file_type')), + ], + }); + + return ( +
+ + +

+ {translate('screens/kyc', 'New name')} +

+ + + + +
+ + +

+ {translate('screens/kyc', 'Proof document')} +

+ +
+ + {error && ( +
+ +
+ )} + + +
+
+ ); +} + function PaymentAgreement({ code, step, onDone }: EditProps): JSX.Element { const { translate, translateError } = useSettingsContext(); const { setPaymentData } = useKyc(); diff --git a/src/translations/languages/de.json b/src/translations/languages/de.json index 01d56ed7..f8431856 100644 --- a/src/translations/languages/de.json +++ b/src/translations/languages/de.json @@ -183,6 +183,12 @@ "https://example.com": "https://beispiel.de", "Mobile number": "Handynummer", "Phone number": "Telefonnummer", + "New address": "Neue Adresse", + "New name": "Neuer Name", + "Proof document": "Nachweis-Dokument", + "Phone number change": "Telefonnummer ändern", + "Address change": "Adressänderung", + "Name change": "Namensänderung", "Organization Information": "Organisation", "Organization name": "Name der Organisation", "Example inc.": "Beispiel GmbH & Co. KG", diff --git a/src/translations/languages/fr.json b/src/translations/languages/fr.json index 59de1a49..f72e59f2 100644 --- a/src/translations/languages/fr.json +++ b/src/translations/languages/fr.json @@ -183,6 +183,12 @@ "https://example.com": "https://exemple.fr", "Mobile number": "Numéro de portable", "Phone number": "Numéro de téléphone", + "New address": "Nouvelle adresse", + "New name": "Nouveau nom", + "Proof document": "Document justificatif", + "Phone number change": "Changement de numéro de téléphone", + "Address change": "Changement d'adresse", + "Name change": "Changement de nom", "Organization Information": "Organisation", "Organization name": "Nom de l'organisation", "Example inc.": "Exempel SARL & Co. KG", diff --git a/src/translations/languages/it.json b/src/translations/languages/it.json index 32ccdcc4..39ad82c5 100644 --- a/src/translations/languages/it.json +++ b/src/translations/languages/it.json @@ -183,6 +183,12 @@ "https://example.com": "https://esempio.it", "Mobile number": "Numero di cellulare", "Phone number": "Numero di telefono", + "New address": "Nuovo indirizzo", + "New name": "Nuovo nome", + "Proof document": "Documento giustificativo", + "Phone number change": "Cambio numero di telefono", + "Address change": "Cambio di indirizzo", + "Name change": "Cambio di nome", "Organization Information": "Informazioni sull'organizzazione", "Organization name": "Nome dell'organizzazione", "Example inc.": "Esempio inc.", From 6dc3e0faf1b5ee0e6eaf253068c7e593c61f009d Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:15:33 +0100 Subject: [PATCH 6/7] fix: add widget build to PR CI and remove unused imports (#993) Add widget build step to PR CI workflow to catch frontend build errors before merging. Remove unused KycChangeAddressData and KycChangeNameData imports from kyc.screen.tsx that caused the DEV build to fail. --- .github/workflows/pr.yml | 3 +++ src/screens/kyc.screen.tsx | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index a4de996f..032ab47f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -41,3 +41,6 @@ jobs: env: REACT_APP_BUILD_ID: ${{ github.run_number }}-${{ github.run_id }} run: npm run build:dev + + - name: Build widget + run: npm run widget:dev diff --git a/src/screens/kyc.screen.tsx b/src/screens/kyc.screen.tsx index 143728ac..60d751b4 100644 --- a/src/screens/kyc.screen.tsx +++ b/src/screens/kyc.screen.tsx @@ -9,8 +9,6 @@ import { GoodsType, KycAddress, KycBeneficialData, - KycChangeAddressData, - KycChangeNameData, KycContactData, KycFinancialOption, KycFinancialQuestion, From 2ffe4edcd7970483202bbf0b2fb342993be4d02e Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 12 Mar 2026 14:57:44 +0100 Subject: [PATCH 7/7] fix: add full prop to phone change StyledInput (#994) Co-authored-by: max-tech-bot --- src/screens/kyc.screen.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/screens/kyc.screen.tsx b/src/screens/kyc.screen.tsx index 60d751b4..3ad43132 100644 --- a/src/screens/kyc.screen.tsx +++ b/src/screens/kyc.screen.tsx @@ -2268,6 +2268,7 @@ function PhoneChangeData({ code, isLoading, step, onDone }: EditProps): JSX.Elem type="tel" label={translate('screens/kyc', 'Phone number')} placeholder="+49 12345678" + full smallLabel />