Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
finwise-miniapp-fe
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ThinhNC
finwise-miniapp-fe
Commits
513e88f6
Commit
513e88f6
authored
Sep 23, 2026
by
ThinhNC
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(handover): display masked email and instructions for production OTP verification
parent
68d16cdd
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
50 additions
and
0 deletions
+50
-0
icons.tsx
src/components/ui/icons.tsx
+19
-0
en.json
src/i18n/locales/en.json
+3
-0
vi.json
src/i18n/locales/vi.json
+3
-0
index.tsx
src/pages/account-handover/index.tsx
+24
-0
handover.ts
src/types/handover.ts
+1
-0
No files found.
src/components/ui/icons.tsx
View file @
513e88f6
...
@@ -391,5 +391,24 @@ export const DownloadIcon: React.FC<IconProps> = ({ size = 20, ...props }) => (
...
@@ -391,5 +391,24 @@ export const DownloadIcon: React.FC<IconProps> = ({ size = 20, ...props }) => (
</
svg
>
</
svg
>
);
);
// Mail / Email Icon
export
const
MailIcon
:
React
.
FC
<
IconProps
>
=
({
size
=
20
,
...
props
})
=>
(
<
svg
width=
{
size
}
height=
{
size
}
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2"
strokeLinecap=
"round"
strokeLinejoin=
"round"
{
...
props
}
>
<
rect
width=
"20"
height=
"16"
x=
"2"
y=
"4"
rx=
"2"
/>
<
path
d=
"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"
/>
</
svg
>
);
src/i18n/locales/en.json
View file @
513e88f6
...
@@ -2453,6 +2453,7 @@
...
@@ -2453,6 +2453,7 @@
"warningNotice2"
:
"All wallets, income/expense history, budgets, and goals will be permanently moved to the new Zalo account."
,
"warningNotice2"
:
"All wallets, income/expense history, budgets, and goals will be permanently moved to the new Zalo account."
,
"warningNotice3"
:
"Your current account will be immediately logged out and securely locked."
,
"warningNotice3"
:
"Your current account will be immediately logged out and securely locked."
,
"warningNotice4"
:
"The QR code and PIN code will automatically expire after 15 minutes."
,
"warningNotice4"
:
"The QR code and PIN code will automatically expire after 15 minutes."
,
"warningNoticeEmail"
:
"An email address must be linked to your account to receive the transfer authorization OTP."
,
"btnInitiate"
:
"Create Code"
,
"btnInitiate"
:
"Create Code"
,
"initiateSuccess"
:
"Handover session created successfully"
,
"initiateSuccess"
:
"Handover session created successfully"
,
"timeLeft"
:
"Time remaining:"
,
"timeLeft"
:
"Time remaining:"
,
...
@@ -2464,6 +2465,8 @@
...
@@ -2464,6 +2465,8 @@
"cancelled"
:
"Handover session cancelled"
,
"cancelled"
:
"Handover session cancelled"
,
"targetConnected"
:
"New Account Connected!"
,
"targetConnected"
:
"New Account Connected!"
,
"otpAuthNotice"
:
"Two-way Mutual Authorization:"
,
"otpAuthNotice"
:
"Two-way Mutual Authorization:"
,
"otpEmailSentTitle"
:
"OTP Sent to Email"
,
"otpEmailSentDesc"
:
"Check your inbox (or Spam/Junk folder) of:"
,
"otpInputLabel"
:
"OTP Authorization Code (6 digits)"
,
"otpInputLabel"
:
"OTP Authorization Code (6 digits)"
,
"otpRequired"
:
"Please enter the 6-digit OTP code"
,
"otpRequired"
:
"Please enter the 6-digit OTP code"
,
"btnConfirmOtp"
:
"Authorize & Transfer Data Now"
,
"btnConfirmOtp"
:
"Authorize & Transfer Data Now"
,
...
...
src/i18n/locales/vi.json
View file @
513e88f6
...
@@ -2554,6 +2554,7 @@
...
@@ -2554,6 +2554,7 @@
"warningNotice2"
:
"Toàn bộ ví tiền, lịch sử thu chi, ngân sách, mục tiêu sẽ được chuyển vĩnh viễn sang tài khoản Zalo mới."
,
"warningNotice2"
:
"Toàn bộ ví tiền, lịch sử thu chi, ngân sách, mục tiêu sẽ được chuyển vĩnh viễn sang tài khoản Zalo mới."
,
"warningNotice3"
:
"Tài khoản hiện tại sẽ tự động đăng xuất và bị khóa an toàn."
,
"warningNotice3"
:
"Tài khoản hiện tại sẽ tự động đăng xuất và bị khóa an toàn."
,
"warningNotice4"
:
"Mã QR và mã PIN sẽ tự động hết hạn sau 15 phút."
,
"warningNotice4"
:
"Mã QR và mã PIN sẽ tự động hết hạn sau 15 phút."
,
"warningNoticeEmail"
:
"Tài khoản cần có liên kết Email để nhận mã xác thực OTP ký duyệt chuyển giao."
,
"btnInitiate"
:
"Tạo Mã"
,
"btnInitiate"
:
"Tạo Mã"
,
"initiateSuccess"
:
"Đã tạo mã chuyển giao thành công"
,
"initiateSuccess"
:
"Đã tạo mã chuyển giao thành công"
,
"timeLeft"
:
"Hiệu lực mã:"
,
"timeLeft"
:
"Hiệu lực mã:"
,
...
@@ -2565,6 +2566,8 @@
...
@@ -2565,6 +2566,8 @@
"cancelled"
:
"Đã hủy phiên chuyển giao"
,
"cancelled"
:
"Đã hủy phiên chuyển giao"
,
"targetConnected"
:
"Tài Khoản Mới Đã Kết Nối!"
,
"targetConnected"
:
"Tài Khoản Mới Đã Kết Nối!"
,
"otpAuthNotice"
:
"Xác thực bảo mật 2 chiều:"
,
"otpAuthNotice"
:
"Xác thực bảo mật 2 chiều:"
,
"otpEmailSentTitle"
:
"Đã gửi mã OTP qua Email"
,
"otpEmailSentDesc"
:
"Kiểm tra hộp thư đến (hoặc hòm thư Rác/Spam) của:"
,
"otpInputLabel"
:
"Mã xác thực OTP (6 chữ số)"
,
"otpInputLabel"
:
"Mã xác thực OTP (6 chữ số)"
,
"otpRequired"
:
"Vui lòng nhập mã OTP gồm 6 chữ số"
,
"otpRequired"
:
"Vui lòng nhập mã OTP gồm 6 chữ số"
,
"btnConfirmOtp"
:
"Ký Duyệt & Chuyển Dữ Liệu Ngay"
,
"btnConfirmOtp"
:
"Ký Duyệt & Chuyển Dữ Liệu Ngay"
,
...
...
src/pages/account-handover/index.tsx
View file @
513e88f6
...
@@ -24,6 +24,7 @@ import {
...
@@ -24,6 +24,7 @@ import {
TransferIcon
,
TransferIcon
,
BudgetIcon
,
BudgetIcon
,
SavingGoalIcon
,
SavingGoalIcon
,
MailIcon
,
}
from
"@/components/ui/icons"
;
}
from
"@/components/ui/icons"
;
import
{
useAuthStore
}
from
"@/stores/auth-store"
;
import
{
useAuthStore
}
from
"@/stores/auth-store"
;
import
{
handoverService
}
from
"@/services/handover.service"
;
import
{
handoverService
}
from
"@/services/handover.service"
;
...
@@ -57,6 +58,7 @@ export const AccountHandoverPage: React.FC = () => {
...
@@ -57,6 +58,7 @@ export const AccountHandoverPage: React.FC = () => {
const
[
targetUserName
,
setTargetUserName
]
=
useState
<
string
>
(
""
);
const
[
targetUserName
,
setTargetUserName
]
=
useState
<
string
>
(
""
);
const
[
otpPrompt
,
setOtpPrompt
]
=
useState
<
string
>
(
""
);
const
[
otpPrompt
,
setOtpPrompt
]
=
useState
<
string
>
(
""
);
const
[
otpDevCode
,
setOtpDevCode
]
=
useState
<
string
>
(
""
);
const
[
otpDevCode
,
setOtpDevCode
]
=
useState
<
string
>
(
""
);
const
[
maskedEmail
,
setMaskedEmail
]
=
useState
<
string
>
(
""
);
const
[
otpInput
,
setOtpInput
]
=
useState
<
string
>
(
""
);
const
[
otpInput
,
setOtpInput
]
=
useState
<
string
>
(
""
);
const
[
senderTimeLeft
,
setSenderTimeLeft
]
=
useState
<
number
>
(
900
);
const
[
senderTimeLeft
,
setSenderTimeLeft
]
=
useState
<
number
>
(
900
);
const
[
isInitiating
,
setIsInitiating
]
=
useState
<
boolean
>
(
false
);
const
[
isInitiating
,
setIsInitiating
]
=
useState
<
boolean
>
(
false
);
...
@@ -162,6 +164,7 @@ export const AccountHandoverPage: React.FC = () => {
...
@@ -162,6 +164,7 @@ export const AccountHandoverPage: React.FC = () => {
if
(
statusRes
.
targetUserName
)
setTargetUserName
(
statusRes
.
targetUserName
);
if
(
statusRes
.
targetUserName
)
setTargetUserName
(
statusRes
.
targetUserName
);
if
(
statusRes
.
otpPrompt
)
setOtpPrompt
(
statusRes
.
otpPrompt
);
if
(
statusRes
.
otpPrompt
)
setOtpPrompt
(
statusRes
.
otpPrompt
);
if
(
statusRes
.
otpCodeDev
)
setOtpDevCode
(
statusRes
.
otpCodeDev
);
if
(
statusRes
.
otpCodeDev
)
setOtpDevCode
(
statusRes
.
otpCodeDev
);
if
(
statusRes
.
sourceUserEmailMasked
)
setMaskedEmail
(
statusRes
.
sourceUserEmailMasked
);
}
else
if
(
statusRes
.
status
===
"EXPIRED"
)
{
}
else
if
(
statusRes
.
status
===
"EXPIRED"
)
{
setSenderStatus
(
"EXPIRED"
);
setSenderStatus
(
"EXPIRED"
);
}
}
...
@@ -222,6 +225,7 @@ export const AccountHandoverPage: React.FC = () => {
...
@@ -222,6 +225,7 @@ export const AccountHandoverPage: React.FC = () => {
setSenderStatus
(
"IDLE"
);
setSenderStatus
(
"IDLE"
);
setInitiateData
(
null
);
setInitiateData
(
null
);
setOtpInput
(
""
);
setOtpInput
(
""
);
setMaskedEmail
(
""
);
openSnackbar
({
openSnackbar
({
type
:
"info"
,
type
:
"info"
,
text
:
t
(
"handover.cancelled"
)
||
"Đã hủy phiên chuyển giao"
,
text
:
t
(
"handover.cancelled"
)
||
"Đã hủy phiên chuyển giao"
,
...
@@ -461,6 +465,10 @@ export const AccountHandoverPage: React.FC = () => {
...
@@ -461,6 +465,10 @@ export const AccountHandoverPage: React.FC = () => {
{
t
(
"handover.warningNotice4"
)
||
{
t
(
"handover.warningNotice4"
)
||
"Mã QR và mã PIN sẽ tự động hết hạn sau 15 phút."
}
"Mã QR và mã PIN sẽ tự động hết hạn sau 15 phút."
}
</
li
>
</
li
>
<
li
>
{
t
(
"handover.warningNoticeEmail"
)
||
"Tài khoản cần có liên kết Email để nhận mã xác thực OTP ký duyệt chuyển giao."
}
</
li
>
</
ul
>
</
ul
>
</
div
>
</
div
>
</
div
>
</
div
>
...
@@ -619,6 +627,22 @@ export const AccountHandoverPage: React.FC = () => {
...
@@ -619,6 +627,22 @@ export const AccountHandoverPage: React.FC = () => {
</
div
>
</
div
>
</
div
>
</
div
>
{
/* Email Sent Banner (Production / Real Flow) */
}
{
maskedEmail
&&
(
<
div
className=
"p-3.5 rounded-clay-sm bg-clay-income/10 border border-clay-income/25 flex items-start gap-2.5"
>
<
MailIcon
size=
{
18
}
className=
"text-clay-income-dark shrink-0 mt-0.5"
/>
<
div
className=
"text-xs text-clay-text leading-relaxed"
>
<
span
className=
"font-bold text-clay-income-dark block"
>
{
t
(
"handover.otpEmailSentTitle"
)
||
"Đã gửi mã OTP qua Email"
}
</
span
>
<
span
>
{
t
(
"handover.otpEmailSentDesc"
)
||
"Kiểm tra hộp thư đến (hoặc hòm thư Rác/Spam) của:"
}{
" "
}
<
strong
className=
"text-clay-primary font-bold"
>
{
maskedEmail
}
</
strong
>
</
span
>
</
div
>
</
div
>
)
}
{
/* Sandbox / Dev OTP Code Notice (if present) */
}
{
/* Sandbox / Dev OTP Code Notice (if present) */
}
{
otpDevCode
&&
(
{
otpDevCode
&&
(
<
div
className=
"p-2.5 rounded-clay-sm bg-clay-income/15 border border-clay-income/30 text-center"
>
<
div
className=
"p-2.5 rounded-clay-sm bg-clay-income/15 border border-clay-income/30 text-center"
>
...
...
src/types/handover.ts
View file @
513e88f6
...
@@ -35,6 +35,7 @@ export interface HandoverStatusResponse {
...
@@ -35,6 +35,7 @@ export interface HandoverStatusResponse {
targetUserName
?:
string
|
null
;
targetUserName
?:
string
|
null
;
otpPrompt
?:
string
;
otpPrompt
?:
string
;
otpCodeDev
?:
string
;
otpCodeDev
?:
string
;
sourceUserEmailMasked
?:
string
;
}
}
export
interface
ClaimHandoverRequest
{
export
interface
ClaimHandoverRequest
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment