Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0ae10cc
feat: Update warning styles and add recipient accuracy reminder in forms
Dprof-in-tech Oct 3, 2025
e8bfba4
Merge branch 'main' into fix-add-reciepient-alert-manual-name-verific…
Dprof-in-tech Nov 5, 2025
6a98e9b
Merge branch 'main' into fix-add-reciepient-alert-manual-name-verific…
Dprof-in-tech Nov 5, 2025
3cda2d4
feat(recipients): enhance recipient name verification and editing fun…
Dprof-in-tech Nov 6, 2025
203188b
fix(TransactionForm): correct conditional rendering for recipient ver…
Dprof-in-tech Nov 6, 2025
44ee74e
fix(RecipientDetailsForm): update feedback message display based on r…
Dprof-in-tech Nov 6, 2025
7050164
fix(RecipientDetailsForm): improve feedback message display logic for…
Dprof-in-tech Nov 6, 2025
d64cfc0
fix(RecipientDetailsForm): refine feedback message display logic for …
Dprof-in-tech Nov 6, 2025
c191119
fix(RecipientDetailsForm): update feedback message display logic for …
Dprof-in-tech Nov 6, 2025
4f7330a
fix(RecipientDetailsForm): implement alert component for recipient ve…
Dprof-in-tech Nov 6, 2025
22dc0a6
fix(RecipientDetailsForm): update styling of 'Learn more' link in ale…
Dprof-in-tech Nov 6, 2025
919e34e
fix(RecipientDetailsForm): refine alert visibility logic based on rec…
Dprof-in-tech Nov 6, 2025
ea01a92
fix(RecipientDetailsForm): include form validation errors in alert vi…
Dprof-in-tech Nov 6, 2025
06dafbf
feat: complete glitchtip integration with sentry wizard (#297)
sundayonah Dec 17, 2025
4553987
feat: enhance user tracking with server-side event logging (#302)
sundayonah Dec 17, 2025
0c61121
refactor: update next.config.mjs for improved configuration management
chibie Dec 17, 2025
a628ef1
feat: add low-memory build support and disable sourcemaps in next.con…
chibie Dec 18, 2025
e6036a3
Fixed a dependncy issue with the Privy-react-auth dependency .
Dprof-in-tech Dec 18, 2025
eae5321
change img to Image tag
Dprof-in-tech Dec 18, 2025
7fe4350
Merge branch 'main' into fix-add-reciepient-alert-manual-name-verific…
Dprof-in-tech Dec 19, 2025
f87d107
add blog link to learn more for accoutnname reconcillation
Dprof-in-tech Dec 19, 2025
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
108 changes: 105 additions & 3 deletions app/components/recipient/RecipientDetailsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { ImSpinner } from "react-icons/im";
import { AnimatePresence, motion } from "framer-motion";
import { useEffect, useMemo, useRef, useState } from "react";
import { ArrowDown01Icon, Tick02Icon } from "hugeicons-react";
import { ArrowDown01Icon, InformationSquareIcon, Tick02Icon } from "hugeicons-react";

import { AnimatedFeedbackItem } from "../AnimatedComponents";
import { InstitutionProps } from "@/app/types";
Expand All @@ -11,6 +11,7 @@ import { fetchAccountName } from "@/app/api/aggregator";
import { usePrivy } from "@privy-io/react-auth";
import { InputError } from "@/app/components/InputError";
import { classNames } from "@/app/utils";
import { trackEvent } from "@/app/hooks/analytics/useMixpanel";
import {
RecipientDetails,
RecipientDetailsFormProps,
Expand Down Expand Up @@ -58,6 +59,7 @@ export const RecipientDetailsForm = ({

const [isFetchingRecipientName, setIsFetchingRecipientName] = useState(false);
const [recipientNameError, setRecipientNameError] = useState("");
const [isRecipientNameEditable, setIsRecipientNameEditable] = useState(false);

const [savedRecipients, setSavedRecipients] = useState<
RecipientDetailsWithId[]
Expand All @@ -68,6 +70,8 @@ export const RecipientDetailsForm = ({
const [recipientToDelete, setRecipientToDelete] =
useState<RecipientDetails | null>(null);

const [alertViewed, setAlertViewed] = useState(false);

const institutionsDropdownRef = useRef<HTMLDivElement>(null);
useOutsideClick({
ref: institutionsDropdownRef,
Expand All @@ -79,6 +83,29 @@ export const RecipientDetailsForm = ({

const prevCurrencyRef = useRef(currency);

// Alert component to avoid duplication and handle analytics
const RecipientAlert = ({ isEditable, message }: { isEditable: boolean; message: string }) => {
const handleLearnMoreClick = (e: React.MouseEvent) => {
e.preventDefault();
trackEvent("recipient_alert_learn_more_clicked", {
alert_type: isEditable ? 'verification_failed' : 'verification_success',
message: message.substring(0, 100), // Truncate for analytics
currency: currency,
institution: selectedInstitution?.name || '',
});
// Allow navigation to continue (could add actual link here)
};

return (
<div className="min-h-[48px] h-fit w-full dark:bg-warning-background/10 bg-warning-background/35 px-3 py-2 rounded-xl flex items-start gap-2">
<InformationSquareIcon className="dark:text-warning-text text-warning-foreground w-[36px] h-[36px] md:w-[24px] md:h-[24px]" />
<p className="text-xs font-light dark:text-warning-text text-warning-foreground leading-tight">
{message} <a href="#" onClick={handleLearnMoreClick} className="text-lavender-500 font-semibold">Learn more.</a>
</p>
</div>
);
};

/**
* Array of institutions filtered and sorted alphabetically based on the bank search term.
*
Expand Down Expand Up @@ -124,6 +151,7 @@ export const RecipientDetailsForm = ({
recipient.name = recipient.name.replace(/\s+/g, " ").trim();
setValue("recipientName", recipient.name, { shouldDirty: true });
setIsManualEntry(false);
setIsRecipientNameEditable(false);
setIsModalOpen(false);
};

Expand Down Expand Up @@ -185,6 +213,27 @@ export const RecipientDetailsForm = ({

// * USE EFFECTS

// Track alert visibility
useEffect(() => {
const shouldShowAlert =
(isRecipientNameEditable && recipientName && !errors.recipientName && !recipientNameError) ||
(!isRecipientNameEditable && recipientName && !recipientNameError);

if (shouldShowAlert && !alertViewed) {
trackEvent("recipient_alert_viewed", {
alert_type: isRecipientNameEditable ? 'verification_failed' : 'verification_success',
has_recipient_name: !!recipientName,
is_editable: isRecipientNameEditable,
currency: currency,
institution: selectedInstitution?.name || '',
});
setAlertViewed(true);
} else if (!shouldShowAlert && alertViewed) {
// Reset viewed state when alert is no longer visible
setAlertViewed(false);
}
}, [isRecipientNameEditable, recipientName, recipientNameError, alertViewed, currency, selectedInstitution?.name]);

useEffect(() => {
let isCancelled = false;

Expand Down Expand Up @@ -238,6 +287,7 @@ export const RecipientDetailsForm = ({
setValue("recipientName", "");
setValue("accountIdentifier", "");
setRecipientNameError("");
setIsRecipientNameEditable(false);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -265,10 +315,20 @@ export const RecipientDetailsForm = ({
institution: institution.toString(),
accountIdentifier: accountIdentifier.toString(),
});
setValue("recipientName", accountName);

// Check if the response is "Ok" which means verification failed but not an error
if (accountName.toLowerCase() === "ok") {
setIsRecipientNameEditable(true);
setValue("recipientName", "");
setRecipientNameError("");
} else {
setIsRecipientNameEditable(false);
setValue("recipientName", accountName);
}
setIsFetchingRecipientName(false);
} catch (error) {
setRecipientNameError("No recipient account found.");
setIsRecipientNameEditable(false);
setIsFetchingRecipientName(false);
}
};
Expand Down Expand Up @@ -316,6 +376,7 @@ export const RecipientDetailsForm = ({
setValue("accountIdentifier", "");
setRecipientNameError("");
setIsManualEntry(true);
setIsRecipientNameEditable(false);
};

// Only clear when currency actually changes (not on mount or preview return)
Expand Down Expand Up @@ -417,7 +478,33 @@ export const RecipientDetailsForm = ({
</div>
) : (
<>
{recipientName ? (
{isRecipientNameEditable ? (
<AnimatedFeedbackItem className="flex-col items-start gap-2">
<input
type="text"
placeholder="Enter recipient name"
{...register("recipientName", {
required: {
value: true,
message: "Recipient name is required",
},
minLength: {
value: 2,
message: "Recipient name must be at least 2 characters",
},
})}
className={classNames(
"w-full rounded-xl border bg-transparent px-4 py-2.5 text-sm outline-none transition-all duration-300 placeholder:text-text-placeholder focus:outline-none dark:text-white/80 dark:placeholder:text-white/30",
errors.recipientName
? "border-input-destructive focus:border-gray-400 dark:border-input-destructive"
: "border-border-input dark:border-white/20 dark:focus:border-white/40 dark:focus:ring-offset-neutral-900",
)}
/>
{errors.recipientName && (
<InputError message={errors.recipientName.message || "Recipient name is required"} />
)}
</AnimatedFeedbackItem>
) : recipientName ? (
<AnimatedFeedbackItem className="justify-between text-gray-400 dark:text-white/50">
<motion.div
className="relative overflow-hidden rounded-lg p-0.5"
Expand All @@ -442,6 +529,7 @@ export const RecipientDetailsForm = ({

<Tick02Icon className="text-lg text-green-700 dark:text-green-500 max-sm:hidden" />
</AnimatedFeedbackItem>

) : recipientNameError ? (
<InputError message={recipientNameError} />
) : null}
Expand All @@ -450,6 +538,20 @@ export const RecipientDetailsForm = ({
</AnimatePresence>
</div>

<AnimatedFeedbackItem>
{isRecipientNameEditable && recipientName && !errors.recipientName && !recipientNameError && (
<RecipientAlert
isEditable={true}
message="Unable to verify details. Ensure the recipient's account number is accurate before proceeding with swap."
/>
)}
{!isRecipientNameEditable && recipientName && !recipientNameError && (
<RecipientAlert
isEditable={false}
message="Make sure the recipient's account number is accurate before proceeding with swap."
/>
)}
</AnimatedFeedbackItem>
<SelectBankModal
isOpen={isSelectBankModalOpen}
onClose={() => setIsSelectBankModalOpen(false)}
Expand Down
4 changes: 2 additions & 2 deletions app/pages/TransactionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ export const TransactionForm = ({
<AnimatePresence>
{currency &&
(authenticated || isInjectedWallet) &&
isUserVerified && (
isUserVerified && (
<AnimatedComponent
variant={slideInOut}
className="space-y-2 rounded-[20px] bg-gray-50 p-2 dark:bg-white/5"
Expand Down Expand Up @@ -779,7 +779,7 @@ export const TransactionForm = ({
/>
</div>
</AnimatedComponent>
)}
)}
</AnimatePresence>

<AnimatePresence>
Expand Down