Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | 3x 3x 3x 2x 1x 3x 3x 3x | import { PropsWithChildren } from "react"; import { LoaderFunctionArgs, redirect, useLoaderData } from "react-router"; import { Authority } from "shared-types"; import { getItem, useGetItem } from "@/api"; import { CardWithTopBorder, ErrorAlert, LoadingSpinner } from "@/components"; import { BreadCrumbs } from "@/components/BreadCrumb"; import { detailsAndActionsCrumbs } from "@/utils"; import { AdminPackageActivities } from "./admin-changes"; import { useDetailsSidebarLinks } from "./hooks"; import { PackageActionsCard } from "./package-actions"; import { PackageActivities } from "./package-activity"; import { PackageDetails } from "./package-details"; import { PackageStatusCard } from "./package-status"; export const DetailCardWrapper = ({ title, children, }: PropsWithChildren<{ title: string; }>) => ( <CardWithTopBorder className="flex-1 min-h-full text-wrap my-0 sm:my-6"> <div className="p-4 py-1 min-h-36"> <h2>{title}</h2> {children} </div> </CardWithTopBorder> ); type DetailsContentProps = { id: string; }; export const DetailsContent = ({ id }: DetailsContentProps) => { const { data: record, isLoading, error } = useGetItem(id); if (isLoading) return <LoadingSpinner />; if (error) return <ErrorAlert error={error} />; const { _source: submission } = record; return ( <div className="w-full py-1 px-4 lg:px-8"> <section id="package_overview" className="flex flex-col sm:flex-row mb-3 sm:mb-0 gap-3 sm:gap-x-[3rem] md:gap-x-[5rem] lg:gap-x-[3rem] xl:gap-x-[6rem]" > <PackageStatusCard submission={submission} /> <PackageActionsCard id={id} submission={submission} /> </section> <div className="flex flex-col gap-3"> <PackageDetails submission={submission} /> <PackageActivities id={id} changelog={submission.changelog} /> <AdminPackageActivities changelog={submission.changelog} /> </div> </div> ); }; type LoaderData = { id: string; authority: Authority; }; export const packageDetailsLoader = async ({ params, }: LoaderFunctionArgs): Promise<LoaderData | Response> => { const { id, authority } = params; if (id === undefined || authority === undefined) { return redirect("/dashboard"); } try { const packageResult = await getItem(id); if (!packageResult || packageResult._source.deleted === true || packageResult.found === false) { return redirect("/dashboard"); } } catch (error) { if (error instanceof Error) { console.log("Error fetching package: ", error.message); } else { console.log("Unknown error fetching package: ", error); } return redirect("/dashboard"); } return { id, authority: authority as Authority }; }; export const Details = () => { const { id, authority } = useLoaderData<LoaderData>(); return ( <div className="max-w-screen-xl mx-auto flex flex-col lg:flex-row"> <div className="px-4 lg:px-8"> <BreadCrumbs options={detailsAndActionsCrumbs({ id, authority })} /> <div className="hidden lg:block pr-8"> <DetailsSidebar id={id} /> </div> </div> <DetailsContent id={id} /> </div> ); }; type DetailsSidebarProps = { id: string; }; const DetailsSidebar = ({ id }: DetailsSidebarProps) => { const links = useDetailsSidebarLinks(id); return ( <aside className="min-w-56 flex-none font-semibold mt-6"> {links.map(({ id, href, displayName }) => ( <a className="block mb-2 text-blue-900" key={id} href={href}> {displayName} </a> ))} </aside> ); }; |