import { Container, Heading, IconButton, Link, Table, TableCaption, TableContainer, Tbody, Td, Th, Thead, Tr, useToast, } from "@chakra-ui/react"; import { useLoaderData } from "@remix-run/react"; export function meta() { return [ { title: "Short Link Manager - Car Crushers", }, ]; } export async function loader({ context }: { context: RequestContext }) { const userId = context.data.current_user?.id; if (!userId) throw new Response(null, { status: 401, }); if ( typeof [0, 2, 4, 5, 6, 7, 9, 10, 11, 12].find( (i) => context.data.current_user.permissions & (1 << i), ) === "undefined" ) throw new Response(null, { status: 403, }); const { results } = await context.env.D1.prepare( "SELECT destination, path FROM short_links WHERE user = ?;", ) .bind(userId) .all(); return results as Record<string, string>[]; } export default function () { const data = useLoaderData<typeof loader>(); const toast = useToast(); async function deleteLink(path: string) { const deleteResp = await fetch( `/api/short-links/${encodeURIComponent(path)}`, { method: "DELETE", }, ); if (!deleteResp.ok) { let error = "Unknown error"; try { error = ((await deleteResp.json()) as { error: string }).error; } catch {} toast({ description: error, status: "error", title: "Failed to delete link", }); return; } toast({ description: `Link ${path} was successfully deleted.`, onCloseComplete: () => location.reload(), status: "success", title: "Deleted", }); } return ( <Container maxW="container.lg"> <Heading mb={8}>Short Links</Heading> <TableContainer mx="16px"> <Table variant="simple"> <TableCaption>Your short links</TableCaption> <Thead> <Tr> <Th>Destination</Th> <Th>Code</Th> <Th>Delete</Th> </Tr> </Thead> <Tbody> {data.map((entry) => { return ( <Tr> <Td>{entry.destination}</Td> <Td> <Link onClick={async () => { await navigator.clipboard.writeText( `https://carcrushe.rs/${encodeURIComponent(entry.path)}`, ); alert("Link copied"); }} > https://carcrushe.rs/{entry.path} </Link> </Td> <Td> <IconButton aria-label="Delete link" colorScheme="red" icon={ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" > <path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z" /> <path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z" /> </svg> } onClick={async () => await deleteLink(entry.path)} /> </Td> </Tr> ); })} </Tbody> </Table> </TableContainer> <Link href="/short-links/create" mt="8px"> Create a new link </Link> </Container> ); }