Files
car-crushers-portal/app/routes/data-request.tsx
T
regalijan 90525f9631
Test, Build, Deploy / Test, Build, and Deploy (push) Failing after 1m22s
Test, Build, Deploy / Create Sentry Release (push) Has been skipped
May as well push now
2026-06-30 21:56:15 -04:00

215 lines
5.9 KiB
TypeScript

import {
Alert,
AlertIcon,
Button,
Card,
Container,
Flex,
FormControl,
FormLabel,
Heading,
Link,
Radio,
RadioGroup,
Spacer,
Stack,
Text,
useToast,
VStack,
} from "@chakra-ui/react";
import { lazy, useState } from "react";
import { useLoaderData } from "@remix-run/react";
const DataRequestFormChunkPurchases = lazy(
() => import("../../components/DataRequestFormChunkPurchases.js"),
);
const DataRequestFormChunkTransfers = lazy(
() => import("../../components/DataRequestFormChunkTransfers.js"),
);
export async function loader({ context }: { context: RequestContext }) {
const cookies = context.request.headers.get("cookie")?.split(/; */);
const dtSession = cookies?.find((c) => c.startsWith("dt-auth="));
if (!cookies || !dtSession)
throw Response.redirect("/data-request/auth", 303);
const sessionId = dtSession.replace("dr-auth=", "");
const sessionData = await context.env.DATA.get(
`data_request_session_${sessionId}`,
);
if (!sessionData) throw Response.redirect("/data-request/auth", 303);
return JSON.parse(sessionData) as { id: number; username: string };
}
export function meta() {
return [
{
title: "Data Request - Car Crushers",
},
];
}
export default function () {
const sessionData = useLoaderData<typeof loader>();
const [requestType, setRequestType] = useState("purchase");
const [files, setFiles] = useState<FileList | null>(null);
const [oldIsTerminated, setOldIsTerminated] = useState(false);
const [processingState, setProcessingState] = useState<string | null>(null);
const [authUrl, setAuthUrl] = useState("");
const toast = useToast();
async function startRequest() {
const body = new FormData();
body.append("type", requestType);
if (files instanceof FileList) {
for (const file of files) {
body.append("file", file);
}
}
const requestStartResponse = await fetch("/api/data-requests/start", {
body,
method: "POST",
redirect: "manual",
});
if (!requestStartResponse.ok) {
let error = "An unknown error has occurred";
try {
error = ((await requestStartResponse.json()) as { error: string })
.error;
} catch {}
toast({
description: error,
status: "error",
title: "Failed to create request",
});
return;
}
const { url } = (await requestStartResponse.json()) as { url?: string };
if (url) {
setAuthUrl(url);
setProcessingState("auth");
} else {
setProcessingState("complete");
}
}
switch (processingState) {
case "auth":
return (
<Container pt="16vh">
<Card borderRadius="32px" p="4vh">
<VStack alignContent="center" gap="2vh">
<Heading>Verify Old Account</Heading>
<br />
<Text>
In order to process your transfer request, you must sign in to
your old Roblox account to verify account ownership. Remember to
switch to the correct account at the prompt.
</Text>
<br />
<Button
as="a"
borderRadius="24px"
colorScheme="blue"
href={authUrl}
>
Verify Old Account
</Button>
</VStack>
</Card>
</Container>
);
case "complete":
return (
<Container
left="50%"
maxW="container.md"
pos="absolute"
top="50%"
transform="translate(-50%, -50%)"
>
<Flex>
<Spacer />
<svg
width="128"
height="128"
fill="currentColor"
viewBox="0 0 16 16"
>
<path d="M2.5 8a5.5 5.5 0 0 1 8.25-4.764.5.5 0 0 0 .5-.866A6.5 6.5 0 1 0 14.5 8a.5.5 0 0 0-1 0 5.5 5.5 0 1 1-11 0z" />
<path d="M15.354 3.354a.5.5 0 0 0-.708-.708L8 9.293 5.354 6.646a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l7-7z" />
</svg>
<Spacer />
</Flex>
<br />
<Heading textAlign="center">Request Submitted</Heading>
<br />
<Text textAlign="center">
We have received your request and will begin processing it shortly.
</Text>
</Container>
);
default:
return (
<Container maxW="container.lg">
<Heading pb="32px" size="xl">
Data Requests
</Heading>
<Alert status="info">
<AlertIcon />
You are signed in to {sessionData.username}.{" "}
<Link
onClick={async () => {
await fetch("/api/data-requests/session", {
method: "DELETE",
});
location.assign("/data-request/auth");
}}
textDecor="underline"
>
Wrong account?
</Link>
</Alert>
<VStack alignItems="start" spacing={12}>
<FormControl isRequired>
<FormLabel>Request Type</FormLabel>
<RadioGroup onChange={setRequestType} value={requestType}>
<Stack>
<Radio value="purchase">Missing Purchase</Radio>
<Radio value="transfer">
Transfer Save Data to New Account
</Radio>
<Radio value="reset">Reset Save Data</Radio>
</Stack>
</RadioGroup>
</FormControl>
{requestType === "purchase" ? (
<DataRequestFormChunkPurchases fileStateSet={setFiles} />
) : null}
{requestType === "transfer" ? (
<DataRequestFormChunkTransfers
fileStateSet={setFiles}
isTerminated={oldIsTerminated}
setIsTerminated={setOldIsTerminated}
/>
) : null}
</VStack>
</Container>
);
}
}