Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
==========
Changelog
==========
+++++++++
v1.0.0 (24/03/2026)
+++++++++

**Added**

- Display tag if sample is in inventory
- Narrow down shipping instructions based on user choice

+++++++++
v0.20.2 (12/03/2026)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const SubmissionOverview = async (props: {
params: Promise<ShipmentParams>;
searchParams: Promise<{ new: string }>;
}) => {
const searchParams = await props.searchParams;
const params = await props.params;
return (
<VStack alignItems='start'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,6 @@ describe("Sample Collection Submission Overview", () => {
expect(screen.getAllByText(/2/i)).toHaveLength(2);
});

it("should display toast if shipment request creation fails", async () => {
server.use(
http.post(
"http://localhost/api/shipments/:shipmentId/request",
() => HttpResponse.json({}, { status: 424 }),
{ once: true },
),
);

render(await SubmissionOverview(baseShipmentParams));

fireEvent.click(screen.getByText("Arrange Shipping"));
fireEvent.click(screen.getByText(/continue/i));

await waitFor(() =>
expect(toastMock).toHaveBeenCalledWith({
status: "error",
title: "Unable to create shipment request",
}),
);
});

it("should display warning if shipment contents are 'locked'", async () => {
server.use(
http.get(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {
VStack,
} from "@chakra-ui/react";
import { Metadata } from "next";
import { ArrangeShipmentButton } from "@/components/navigation/ArrangeShipmentButton";
import { getShipmentData } from "@/utils/server/shipment";
import { ShippingInstructions } from "./pageContent";

export const metadata: Metadata = {
title: "Sample Collection Submitted - Scaup",
Expand Down Expand Up @@ -101,39 +101,7 @@ const SubmissionOverview = async (props: { params: Promise<ShipmentParams> }) =>
<DynamicFormView formType={shipmentData.formModel} data={shipmentData.counts} />
</VStack>

<Text fontSize='18px' mt='1em'>
If you <b>do not plan to use Diamond&#39;s own courier</b> (DHL, on Diamond&#39;s
account), you <b>do not need to arrange shipping</b> through Diamond. When using your
own courier, ensure the labels provided by your courier are securely affixed.
</Text>
<Text fontSize='18px'>
If you plan to arrange shipping through Diamond,{" "}
<b>
print the tracking labels after you&#39;re finished setting up your shipping details
</b>
. This can be done on the{" "}
<Link
textDecoration='underline'
color='diamond.600'
href={`/proposals/${params.proposalId}/sessions/${params.visitNumber}/shipments/${params.shipmentId}`}
>
sample collection summary page
</Link>
. You will be automatically redirected to that page once you finish setting up
shipping.
</Text>
<Text fontSize='18px'>
Tracking labels <b>must</b> be securely affixed to the outside of both dewars and
dewar cases, even if using your own courier.
</Text>
<HStack>
<ArrangeShipmentButton params={params} isBooked={shipmentData.isBooked} />
<NextLink
href={`${process.env.NEXT_PUBLIC_API_URL}/shipments/${params.shipmentId}/tracking-labels`}
>
<Button>Print Tracking Labels</Button>
</NextLink>
</HStack>
<ShippingInstructions params={params} isBooked={shipmentData.isBooked} />
</>
)}
</VStack>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { fireEvent, render, screen, waitFor } from "@testing-library/react";

import { toastMock } from "@/../vitest.setup";
import { server } from "@/mocks/server";
import { HttpResponse, http } from "msw";
import { ShippingInstructions } from "./pageContent";

const params = {
proposalId: "cm1",
visitNumber: "1",
shipmentId: "1",
};

describe("Shipping Instructions", () => {
it("should select 'yes' for shipping through Diamond if already booked", async () => {
render(<ShippingInstructions params={params} isBooked={true} />);

expect(screen.getByText("Yes")).toHaveAttribute("data-checked");
});

it("should display label printing instructions if 'no' is selected", async () => {
render(<ShippingInstructions params={params} isBooked={false} />);

fireEvent.click(screen.getByText("No"));
expect(screen.getByText("Print Tracking Labels")).toBeInTheDocument();
});

it("should display toast if shipment request creation fails", async () => {
server.use(
http.post(
"http://localhost/api/shipments/:shipmentId/request",
() => HttpResponse.json({}, { status: 424 }),
{ once: true },
),
);

render(<ShippingInstructions params={params} isBooked={false} />);

fireEvent.click(screen.getByText("Yes"));
fireEvent.click(screen.getByText("Arrange Shipping"));
fireEvent.click(screen.getByText(/continue/i));

await waitFor(() =>
expect(toastMock).toHaveBeenCalledWith({
status: "error",
title: "Unable to create shipment request",
}),
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"use client";
import { ShipmentParams } from "@/types/generic";
import {
Box,
Button,
Divider,
Heading,
HStack,
Link,
Radio,
RadioGroup,
Stack,
Text,
} from "@chakra-ui/react";
import { ArrangeShipmentButton } from "@/components/navigation/ArrangeShipmentButton";
import { useState } from "react";
import NextLink from "next/link";

export interface ShippingInstructionsProps {
params: ShipmentParams;
isBooked: boolean;
}

export const ShippingInstructions = ({ params, isBooked }: ShippingInstructionsProps) => {
const [isBookedThroughDiamond, setIsBookedThroughDiamond] = useState<string | null>(null);
return (
<>
<Heading size='lg'>Shipping Options</Heading>
<Divider />
<Text>
I will be shipping my dewar using{" "}
<b>Diamond&#39;s own courier (DHL, on Diamond&#39;s account)</b>
</Text>
<RadioGroup onChange={setIsBookedThroughDiamond} defaultValue={isBooked ? "true" : undefined}>
<Stack spacing={5} direction='row'>
<Radio borderColor='black' size='lg' value='true'>
Yes
</Radio>
<Radio borderColor='black' size='lg' value='false' isDisabled={isBooked}>
No
</Radio>
</Stack>
</RadioGroup>

{isBookedThroughDiamond !== null || isBooked ? (
<Box borderLeft='4px solid black' p='1em' mb='1em'>
{" "}
{isBookedThroughDiamond === "false" && !isBooked ? (
<>
<Text fontSize='18px'>
When using your own courier, ensure the labels provided by your courier are securely
affixed
</Text>
<Text fontSize='18px' mt='1em'>
Tracking labels <b>must</b> be securely affixed to the outside of both dewars and
dewar cases, even if using your own courier.
</Text>
<NextLink
href={`${process.env.NEXT_PUBLIC_API_URL}/shipments/${params.shipmentId}/tracking-labels`}
>
<Button mt='1em'>Print Tracking Labels</Button>
</NextLink>
</>
) : (
<>
<Text fontSize='18px'>
<b>
Print the tracking labels after you&#39;re finished setting up your shipping
details
</b>
. This can be done on the{" "}
<Link
textDecoration='underline'
color='diamond.600'
href={`/proposals/${params.proposalId}/sessions/${params.visitNumber}/shipments/${params.shipmentId}/booking-and-labels`}
>
booking and labels page
</Link>
. You will be automatically redirected to that page once you finish setting up
shipping.
</Text>
<HStack mt='1em'>
<ArrangeShipmentButton params={params} isBooked={isBooked} />
</HStack>
</>
)}
</Box>
) : null}
</>
);
};
17 changes: 17 additions & 0 deletions src/components/navigation/SampleCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,23 @@ describe("Sample Card", () => {
);
});

it("should display tag if item is in inventory", () => {
renderWithProviders(
<SampleCard
sample={{
...baseSample,
containerName: "inventory_gridbox",
containerId: 1,
isInternal: true,
originSamples: [{ ...baseSample, name: "parent-sample" }],
}}
params={params}
/>,
);

expect(screen.getByText("In Inventory")).toBeInTheDocument();
});

it("should display multiple parents", () => {
renderWithProviders(
<SampleCard
Expand Down
5 changes: 5 additions & 0 deletions src/components/navigation/SampleCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ export const SampleCard = ({ sample, params }: SampleCardProps) => {
{sample.containerId && sample.containerName ? (
<StatHelpText m='0'>
In <Link href={`/containers/${sample.containerId}`}>{sample.containerName}</Link>{" "}
{sample.isInternal && (
<Tag colorScheme='red' size='sm'>
In Inventory
</Tag>
)}
{sample.location && `, slot ${sample.location}`}
</StatHelpText>
) : (
Expand Down
Loading
Loading