Skip to content

Commit

Permalink
Implement account migration after initial signup
Browse files Browse the repository at this point in the history
  • Loading branch information
ccruzkauppila committed Nov 5, 2024
1 parent 758a17f commit 2bc8bc6
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 1 deletion.
67 changes: 67 additions & 0 deletions src/app/(loggedin)/account/AccountMigrationDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useActionState, useEffect, useRef, useState } from "react";
import toast from "react-hot-toast";

import { AnimatedPopup, PopupRefActions } from "@/components/ui/AnimatedPopup";
import { FatButton } from "@/components/ui/Buttons/FatButton";
import { LineButton } from "@/components/ui/Buttons/LineButton";
import { DialogTitleWithButton } from "@/components/ui/DialogTitleWithButton";
import { MigrationCombobox } from "@/components/ui/MigrationCombobox";
import { migrateAccountAction } from "@/server/actions/account/migration";

export const AccountMigrationDialog = () => {
const [step, setStep] = useState(0);
const [result, formAction, isPending] = useActionState(migrateAccountAction, {
success: false,
error: undefined,
});

useEffect(() => {
if (result?.error) {
toast.error(result.error);
} else if (result?.success) {
toast.success("Account migrated successfully!");
closeModal();
}
}, [result]);
const popupRef = useRef<PopupRefActions>(undefined);
const closeModal = () => {
popupRef?.current?.closeContainer();
setStep(0);
};

const setupButton = (
<LineButton text="Migrate old account" buttonType="button" />
);

return (
<AnimatedPopup ref={popupRef} TriggerComponent={setupButton}>
<div className="flex flex-col items-center gap-4 px-6 py-6 md:gap-12 md:px-16 md:py-12">
<DialogTitleWithButton
title="Account migration"
onButtonClick={closeModal}
/>
<p className="text-md text-neutral-700 md:text-xl ">
Have an old Namukilke account you want to migrate? Use the form below
to move your old account's funds to this one. <br />
</p>
<p className="text-md text-red-400 md:text-xl">
Note that you can only migrate one account.
</p>
<form
action={formAction}
className="flex flex-col items-center gap-4 md:gap-12"
>
<MigrationCombobox />
<FatButton
buttonType="button"
type="submit"
text="Migrate account"
intent="primary"
loading={isPending}
fullwidth
/>
</form>
</div>
</AnimatedPopup>
);
};
12 changes: 11 additions & 1 deletion src/app/(loggedin)/account/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ import { RfidSetupDialog } from "@/components/ui/RfidSetupDialog";
import { SectionTitle } from "@/components/ui/SectionTitle";
import { getCurrentUserBalance } from "@/server/actions/account/getBalance";
import { logoutAction } from "@/server/actions/auth/logout";
import { getCurrentUser } from "@/server/db/queries/account";
import {
getCurrentUser,
getCurrentUserMigrationStatus,
} from "@/server/db/queries/account";

import { AccountMigrationDialog } from "./AccountMigrationDialog";

const AccountPage = () => {
const pathName = usePathname();
const [nfcConnectionStatus, setNfcConnectionStatus] = useState<string | null>(
null,
);
const [userBalance, setUserBalance] = useState<string | null>(null);
const [userMigrated, setUserMigrated] = useState<boolean>(true);
useEffect(() => {
const checkNfcConnection = async () => {
const user = await getCurrentUser();
Expand All @@ -38,6 +44,9 @@ const AccountPage = () => {
getCurrentUserBalance().then((balance) => {
setUserBalance(formatCurrency(balance));
});
getCurrentUserMigrationStatus().then((migrated) => {
setUserMigrated(migrated);
});
}, []);
return (
<div className="flex h-full w-full flex-grow flex-col justify-between gap-6 bg-white py-8 md:py-12 ">
Expand Down Expand Up @@ -70,6 +79,7 @@ const AccountPage = () => {
</AddFundsDialog>

<RfidSetupDialog />
{!userMigrated && <AccountMigrationDialog />}
<LineButton
text="Purchase history"
buttonType="a"
Expand Down
57 changes: 57 additions & 0 deletions src/server/actions/account/migration.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
"use server";

import { revalidatePath } from "next/cache";

import { getSession } from "@/auth/ironsession";
import { ClientLegacyUser } from "@/common/types";
import { db } from "@/server/db/prisma";
import { newDeposit } from "@/server/db/queries/deposit";
import { ValueError } from "@/server/exceptions/exception";
import { Prisma, PrismaClient } from "@prisma/client";

export const getUnmigratedAccounts = async (): Promise<ClientLegacyUser[]> => {
const users = await db.legacyUser.findMany({
Expand All @@ -15,3 +21,54 @@ export const getUnmigratedAccounts = async (): Promise<ClientLegacyUser[]> => {
balance: user.balance.toNumber(),
}));
};

export const migrateAccountAction = async (
prevState: any,
formData: FormData,
) => {
try {
const session = await getSession();
const currentUserId = session?.user?.userId;
if (!currentUserId) {
throw new Error("Unauthorized");
}
const formUserId = formData.get("legacyAccountId") as string;
const legacyId = parseInt(formUserId);
if (!legacyId) {
throw new ValueError({
cause: "invalid_value",
message: "Couldn't find legacy user to migrate",
});
}

await db.$transaction(async (tx) => {
const legacyUser = await tx.legacyUser.update({
where: {
id: legacyId,
},
data: {
newAccountId: currentUserId,
},
});
if (!legacyUser) {
throw new ValueError({
cause: "invalid_value",
message: "Couldn't find legacy user to migrate",
});
}
const legacyBalance = legacyUser.balance.toNumber();
await newDeposit(tx as PrismaClient, currentUserId, legacyBalance);
});
revalidatePath("/account");
return { success: true };
} catch (error: any) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === "P2002") {
return {
error: "You've already migrated a legacy account!",
};
}
}
return { error: error?.message || "Unknown error occurred" };
}
};
12 changes: 12 additions & 0 deletions src/server/db/queries/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,15 @@ export const migrateLegacyUser = async (
},
});
};

export const getCurrentUserMigrationStatus = async () => {
const user = await getCurrentUser();
if (!user.ok) return false;
const legacyUser = await db.legacyUser.findFirst({
where: {
newAccountId: user.user.id,
},
});
console.log("got legacy user", legacyUser);
return !!legacyUser;
};

0 comments on commit 2bc8bc6

Please sign in to comment.