diff --git a/README.md b/README.md index 3f9d5c8..8b691b8 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ First, ensure that you have the [Unified Ecommerce Bakend](https://github.com/mi Environment variables are described in detail in `env/env.defaults.public`; all env vars should have functional defaults. However, a few dependencies to note: -- In the Unified Ecommerce backend, `MITOL_UE_PAYMENT_BASKET_ROOT` and `MITOL_UE_PAYMENT_BASKET_CHOOSER` should point to the this repo's frontend. (e.g., `https://uefe.odl.local:8072`) +- In the Unified Ecommerce backend, `MITOL_UE_PAYMENT_BASKET_ROOT` and `MITOL_UE_PAYMENT_BASKET_CHOOSER` should point to the this repo's frontend. (e.g., `https://ue.odl.local:8072`) ### Run the app #### With Docker -With `docker compose up`, you should be up and running. Visit the application at http://uefe.odl.local:8072 +With `docker compose up`, you should be up and running. Visit the application at http://ue.odl.local:8072 #### Without Docker @@ -25,8 +25,10 @@ You can run the app outside of docker. This may be faster and more convenient. T 1. Some way to load environment variables. [direnv](https://direnv.net/) is a great tool for this; a sample `.envrc` file is committed in the repo. 2. A NodeJS runtime; [`nvm`](https://github.com/nvm-sh/nvm) is a simple tool for managing NodeJS versions. -With that done, `yarn start`, `yarn install`, and visit http://uefe.odl.local:8072 +With that done, `yarn start`, `yarn install`, and visit http://ue.odl.local:8072 ## Accessing the Application -The Unified Ecommerce backend uses same-site cookies for authentication. Therefore, the frontend client must run on the "same site" as the backend. Therefore, if the backend runs on `ue.odl.local:8073`, you **must** access the frontend on at a hostname such as `uefe.odl.local` (or `*.odl.local`), _not_ `localhost`. +The Unified Ecommerce backend uses same-site cookies for authentication. Therefore, the frontend client must run on the "same site" as the backend. Therefore, if the backend runs on `ue.odl.local:8073`, you **must** access the frontend on at a hostname such as `ue.odl.local` (or `*.odl.local`), _not_ `localhost`. + +To prevent CORS and CSRF errors, set the frontend and backend URLs to be either the same hostname (`ue.odl.local`) or set the backend to be a subdomain of the frontend (`api.ue.odl.local` and `ue.odl.local` - this is closer to the actual deployment). diff --git a/package.json b/package.json index 81e7a78..8642786 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@faker-js/faker": "^9.0.0", - "@mitodl/smoot-design": "^1.0.1", + "@mitodl/smoot-design": "^1.1.1", "@mui/material": "^6.1.8", "@mui/material-nextjs": "^6.1.8", "@remixicon/react": "^4.2.0", diff --git a/src/app/cart/page.tsx b/src/app/cart/page.tsx deleted file mode 100644 index 8c95572..0000000 --- a/src/app/cart/page.tsx +++ /dev/null @@ -1,25 +0,0 @@ -"use client"; -import { useSearchParams } from "next/navigation"; - -const Cart = () => { - const searchParams = useSearchParams(); - return ( -
-

Cart

-
{JSON.stringify(Object.fromEntries(searchParams), null, 2)}
-
{ - event.preventDefault(); - const formData = new FormData(event.currentTarget); - const params = formData.get("params"); - window.history.pushState(null, "", `?${params}`); - }} - > - - -
-
- ); -}; - -export default Cart; diff --git a/src/app/checkout/page.tsx b/src/app/checkout/page.tsx deleted file mode 100644 index 40ee1c1..0000000 --- a/src/app/checkout/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -const Checkout = () => { - return ( -
-

Checkout

-
- ); -}; - -export default Checkout; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 34d8326..897cb67 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,6 @@ import type { Metadata } from "next"; import { Suspense } from "react"; -import Header from "@/page-components/Header/Header"; +import Header from "@/components/Header/Header"; import EnsureSession from "@/page-components/EnsureSession/EnsureSession"; import "./global.css"; import Providers from "./providers"; diff --git a/src/app/page.tsx b/src/app/page.tsx index 3e3ad33..fe07350 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,8 +1,159 @@ +"use client"; +import React from "react"; +import { useSearchParams, useRouter } from "next/navigation"; +import { useMetaIntegratedSystemsList } from "@/services/ecommerce/meta/hooks"; +import { styled } from "@mitodl/smoot-design"; +import { Typography } from "@mui/material"; +import Container from "@mui/material/Container"; +import { getCurrentSystem } from "@/utils/system"; +import { Card } from "@/components/Card/Card"; +import CartItem from "@/page-components/CartItem/CartItem"; +import CartSummary from "@/page-components/CartSummary/CartSummary"; +import StyledCard from "@/components/Card/StyledCard"; +import { UseQueryResult } from "@tanstack/react-query"; +import type { + PaginatedBasketWithProductList, + BasketWithProduct, +} from "@/services/ecommerce/generated/v0"; + +import { + usePaymentsBasketList, + usePaymentsBasketRetrieve, +} from "@/services/ecommerce/payments/hooks"; +import { + BasketItemWithProduct, + IntegratedSystem, +} from "@/services/ecommerce/generated/v0"; + +type CartProps = { + system: string; +}; + +type CartBodyProps = { + systemId: number; +}; + +const SelectSystemContainer = styled.div` + margin: 16px 0; +`; + +const SelectSystem: React.FC = () => { + const systems = useMetaIntegratedSystemsList(); + const router = useRouter(); + + const hndSystemChange = (ev: React.ChangeEvent) => { + router.push(`/?system=${ev.target.value}`); + }; + + return ( + + {systems.isFetched && systems.data ? ( + <> + + + + ) : ( +

Loading systems...

+ )} +
+ ); +}; + +const CartContainer = styled.div``; + +const CartBodyContainer = styled.div` + width: 100%; + display: flex; + gap: 48px; + align-items: start; +`; + +const CartHeader = styled.div` + width: 100%; + flex-grow: 1; + margin-bottom: 16px; +`; + +const CartItemsContainer = styled.div` + width: auto; + flex-grow: 1; +`; + +const CartBody: React.FC = ({ systemId }) => { + const basket = usePaymentsBasketList({ + integrated_system: systemId, + }) as UseQueryResult; + const basketDetails = usePaymentsBasketRetrieve( + basket.data?.results[0]?.id || 0, + { enabled: !!basket.data?.count }, + ) as UseQueryResult; + + return basketDetails.isFetched && + basketDetails?.data?.basket_items && + basketDetails.data.basket_items.length > 0 ? ( + + + {basketDetails.data.basket_items.map((item: BasketItemWithProduct) => ( + + ))} + + + + ) : ( + + + +

Your cart is empty.

+
+
+
+ ); +}; + +const Cart: React.FC = ({ system }) => { + const systems = useMetaIntegratedSystemsList(); + const selectedSystem = systems.data?.results.find( + (integratedSystem: IntegratedSystem) => integratedSystem.slug === system, + ); + + return ( + selectedSystem && ( + + + + You are about to purchase the following: + + + {selectedSystem && } + + ) + ); +}; + const Home = () => { + const searchParams = useSearchParams(); + const specifiedSystem = getCurrentSystem(searchParams); + return ( -
-

Home

-
+ + {specifiedSystem === "" && ( + + + We can't determine what system you're trying to access. Please + choose a system to continue. + + + + )} + {specifiedSystem !== "" && } + ); }; diff --git a/src/components/Card/Card.test.tsx b/src/components/Card/Card.test.tsx new file mode 100644 index 0000000..29689fb --- /dev/null +++ b/src/components/Card/Card.test.tsx @@ -0,0 +1,31 @@ +import { render } from "@testing-library/react"; +import { Card } from "./Card"; + +describe("Card", () => { + test("has class MitCard-root on root element", () => { + const { container } = render( + + Title + + Info + Footer + Actions + , + ); + const card = container.firstChild as HTMLElement; + const title = card.querySelector(".MitCard-title"); + const image = card.querySelector(".MitCard-image"); + const info = card.querySelector(".MitCard-info"); + const footer = card.querySelector(".MitCard-footer"); + const actions = card.querySelector(".MitCard-actions"); + + expect(card).toHaveClass("MitCard-root"); + expect(card).toHaveClass("Foo"); + expect(title).toHaveTextContent("Title"); + expect(image).toHaveAttribute("src", "https://via.placeholder.com/150"); + expect(image).toHaveAttribute("alt", "placeholder"); + expect(info).toHaveTextContent("Info"); + expect(footer).toHaveTextContent("Footer"); + expect(actions).toHaveTextContent("Actions"); + }); +}); diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx new file mode 100644 index 0000000..0621f91 --- /dev/null +++ b/src/components/Card/Card.tsx @@ -0,0 +1,258 @@ +import { + FC, + ReactNode, + Children, + ImgHTMLAttributes, + isValidElement, + CSSProperties, +} from "react"; +import styled from "@emotion/styled"; +import { pxToRem } from "../../utils/misc"; +import Link from "next/link"; +import { createTheme } from "@mitodl/smoot-design"; + +export type Size = "small" | "medium"; + +const theme = createTheme(); + +/* + *The relative positioned wrapper allows the action buttons to live adjacent to the + * Link container in the DOM structure. They cannot be a descendent of it as + * buttons inside anchors are not valid HTML. + */ +export const Wrapper = styled.div<{ size?: Size }>` + position: relative; + ${({ size }) => { + let width; + if (!size) return ""; + if (size === "medium") width = 300; + if (size === "small") width = 192; + return ` + min-width: ${width}px; + max-width: ${width}px; + `; + }} +`; + +export const containerStyles = ` + border-radius: 8px; + border: 1px solid ${theme.custom.colors.lightGray2}; + background: ${theme.custom.colors.white}; + overflow: hidden; +`; + +const LinkContainer = styled(Link)` + ${containerStyles} + display: block; + position: relative; + + :hover { + text-decoration: none; + border-color: ${theme.custom.colors.silverGrayLight}; + box-shadow: + 0 2px 4px 0 rgb(37 38 43 / 10%), + 0 2px 4px 0 rgb(37 38 43 / 10%); + cursor: pointer; + } +`; + +const Container = styled.div` + ${containerStyles} + display: block; + position: relative; +`; + +const Content = () => <>; + +const Body = styled.div` + margin: 16px; +`; + +const Image = styled.img<{ height?: number | string; size?: Size }>` + display: block; + width: 100%; + height: ${({ height, size }) => + height ?? (size === "small" ? "120px" : "170px")}; + background-color: ${theme.custom.colors.lightGray1}; + object-fit: cover; +`; + +const Info = styled.div<{ size?: Size }>` + ${{ ...theme.typography.subtitle3 }} + color: ${theme.custom.colors.silverGrayDark}; + display: flex; + justify-content: space-between; + margin-bottom: ${({ size }) => (size === "small" ? 4 : 8)}px; +`; + +const Title = styled.span<{ lines?: number; size?: Size }>` + text-overflow: ellipsis; + height: ${({ lines, size }) => { + const lineHeightPx = size === "small" ? 18 : 20; + lines = lines ?? (size === "small" ? 2 : 3); + return theme.typography.pxToRem(lines * lineHeightPx); + }}; + overflow: hidden; + margin: 0; + + ${({ size }) => + size === "small" + ? { ...theme.typography.subtitle2 } + : { ...theme.typography.subtitle1 }} + + ${({ lines, size }) => { + lines = lines ?? (size === "small" ? 2 : 3); + return ` + @supports (-webkit-line-clamp: ${lines}) { + white-space: initial; + display: -webkit-box; + -webkit-line-clamp: ${lines}; + -webkit-box-orient: vertical; + }`; + }} +`; + +const Footer = styled.span` + display: block; + height: ${pxToRem(16)}; + ${{ + ...theme.typography.body3, + color: theme.custom.colors.silverGrayDark, + }} +`; + +const Bottom = styled.div` + display: flex; + justify-content: space-between; + align-items: flex-end; + margin: 0 16px 16px; + height: 32px; +`; + +const Actions = styled.div` + display: flex; + gap: 8px; + position: absolute; + bottom: 16px; + right: 16px; +`; + +type CardProps = { + children: ReactNode[] | ReactNode; + className?: string; + size?: Size; + href?: string; +}; + +type ImageProps = ImgHTMLAttributes & { + size?: Size; + height?: number | string; + style?: CSSProperties; +}; +type TitleProps = { + children?: ReactNode; + lines?: number; + style?: CSSProperties; +}; +type SlotProps = { children?: ReactNode; style?: CSSProperties }; + +type Card = FC & { + Content: FC<{ children: ReactNode }>; + Image: FC; + Info: FC; + Title: FC; + Footer: FC; + Actions: FC; +}; + +const Card: Card = ({ children, className, size, href }) => { + let content, + image: ImageProps = {}, + info: SlotProps = {}, + title: TitleProps = {}, + footer: SlotProps = {}, + actions: SlotProps = {}; + + const _Container = href ? LinkContainer : Container; + + /* + * Allows rendering child elements to specific "slots": + * + * + * The Title + * + * + * + * Akin to alternative interface: + * The Title} image={} />. + * + * An RFC here provides rationale: https://github.com/nihgwu/rfcs/blob/neo/slots/text/0000-slots.md + */ + Children.forEach(children, (child) => { + if (!isValidElement(child)) return; + if (child.type === Content) content = child.props.children; + else if (child.type === Image) image = child.props; + else if (child.type === Info) info = child.props; + else if (child.type === Title) title = child.props; + else if (child.type === Footer) footer = child.props; + else if (child.type === Actions) actions = child.props; + }); + + const allClassNames = ["MitCard-root", className ?? ""].join(" "); + + if (content) { + return ( + + <_Container className={className} href={href!}> + {content} + + + ); + } + + return ( + + <_Container href={href!}> + {image && ( + // alt text will be checked on Card.Image + // eslint-disable-next-line styled-components-a11y/alt-text + )} + /> + )} + + {info.children && ( + + {info.children} + + )} + + {title.children} + + + +
+ {footer.children} +
+
+ + {actions.children && ( + + {actions.children} + + )} +
+ ); +}; + +Card.Content = Content; +Card.Image = Image; +Card.Info = Info; +Card.Title = Title; +Card.Footer = Footer; +Card.Actions = Actions; + +export { Card }; diff --git a/src/components/Card/StyledCard.tsx b/src/components/Card/StyledCard.tsx new file mode 100644 index 0000000..2266889 --- /dev/null +++ b/src/components/Card/StyledCard.tsx @@ -0,0 +1,8 @@ +import { styled } from "@mitodl/smoot-design"; +import { Card } from "./Card"; + +const StyledCard = styled(Card)` + padding: 32px; +`; + +export default StyledCard; diff --git a/src/page-components/Header/Header.tsx b/src/components/Header/Header.tsx similarity index 100% rename from src/page-components/Header/Header.tsx rename to src/components/Header/Header.tsx diff --git a/src/components/TruncateText/TruncateText.mdx b/src/components/TruncateText/TruncateText.mdx new file mode 100644 index 0000000..3ca43a0 --- /dev/null +++ b/src/components/TruncateText/TruncateText.mdx @@ -0,0 +1,21 @@ +import { Canvas, Meta } from "@storybook/blocks"; + +import * as TruncateTextStories from "./TruncateText.stories"; + + + +# TruncateText + +Use `lineClamp` to truncate text after a certain number of lines: + +`lineClamp={1}` + + + +`lineClamp={3}` + + + +`lineClamp={"none"}` + + diff --git a/src/components/TruncateText/TruncateText.tsx b/src/components/TruncateText/TruncateText.tsx new file mode 100644 index 0000000..3ccc224 --- /dev/null +++ b/src/components/TruncateText/TruncateText.tsx @@ -0,0 +1,34 @@ +import { css } from "@emotion/react"; +import styled from "@emotion/styled"; + +type TruncateTextProps = { + /** + * Number of lines to display before truncating text. + */ + lineClamp?: number | "none"; +}; + +const truncateText = (lines?: number | "none") => + css({ + display: "-webkit-box", + WebkitBoxOrient: "vertical", + overflow: "hidden", + textOverflow: "ellipsis", + WebkitLineClamp: lines, + [`@supports (-webkit-line-clamp: ${lines})`]: { + whiteSpace: "initial", + display: "-webkit-box", + WebkitLineClamp: `${lines}`, // cast to any to avoid typechecking error in lines, + WebkitBoxOrient: "vertical", + }, + }); + +/** + * Truncate the content after specified number of lines. + */ +const TruncateText = styled.div(({ lineClamp: lines }) => + truncateText(lines), +); + +export { TruncateText, truncateText }; +export type { TruncateTextProps }; diff --git a/src/jest-setup.ts b/src/jest-setup.ts index 2e88c1a..5fa794a 100644 --- a/src/jest-setup.ts +++ b/src/jest-setup.ts @@ -2,6 +2,7 @@ import * as matchers from "jest-extended"; import failOnConsole from "jest-fail-on-console"; import { mockAxiosInstance } from "./test-utils/mockAxios"; import { resetAllWhenMocks } from "jest-when"; +import "@testing-library/jest-dom"; expect.extend(matchers); failOnConsole(); diff --git a/src/page-components/CartItem/CartItem.tsx b/src/page-components/CartItem/CartItem.tsx new file mode 100644 index 0000000..be858e9 --- /dev/null +++ b/src/page-components/CartItem/CartItem.tsx @@ -0,0 +1,83 @@ +import { BasketItemWithProduct } from "@/services/ecommerce/generated/v0"; +import { styled } from "@mitodl/smoot-design"; +import { Card } from "../../components/Card/Card"; +import { Typography } from "@mui/material"; + +import StyledCard from "../../components/Card/StyledCard"; + +type CartItemProps = { + item: BasketItemWithProduct; +}; + +const CartItemContainer = styled.div(() => ({ + display: "flex", + marginTop: "16px", + marginBottom: "8px", +})); + +const CartItemImage = styled.img` + width: 192px; + margin: 0; +`; + +const CartItemContentContainer = styled.div` + width: auto; + flex-grow: 1; + margin: 0; + margin-left: 32px; +`; + +const CartItemProductMeta = styled.div` + display: flex; + align-items: start; + margin-bottom: 8px; +`; + +const CartItemProductPrice = styled.div` + margin-left: auto; + ${({ theme }) => ({ ...theme.typography.h5 })}; +`; + +const CartItemProductSku = styled.div` + ${({ theme }) => ({ ...theme.typography.body1 })}; + color: ${({ theme }) => theme.custom.colors.silverGrayLight}; +`; + +const CartItemProductDescription = styled.div` + margin: 8px 0; +`; + +const CartItem: React.FC = ({ item }) => { + return ( + + + + + + + + {item.product.sku} + + {parseFloat(item.product.price).toLocaleString("en-US", { + style: "currency", + currency: "USD", + })} + + + + {item.product.name} + + + {item.product.description} + + + + + + ); +}; + +export default CartItem; diff --git a/src/page-components/CartSummary/CartSummary.tsx b/src/page-components/CartSummary/CartSummary.tsx new file mode 100644 index 0000000..e748e46 --- /dev/null +++ b/src/page-components/CartSummary/CartSummary.tsx @@ -0,0 +1,168 @@ +"use client"; +import React from "react"; +import { Button, styled } from "@mitodl/smoot-design"; +import { Typography } from "@mui/material"; +import { TextField } from "@mitodl/smoot-design"; +import { Card } from "../../components/Card/Card"; +import StyledCard from "../../components/Card/StyledCard"; +import CartSummaryItem, { + CartSummaryItemContainer, + CartSummaryItemTitle, + CartSummaryItemValue, +} from "../CartSummaryItem/CartSummaryItem"; +import PlaceOrderButton from "../PlaceOrderButton/PlaceOrderButton"; + +import { + usePaymentsBasketRetrieve, + usePaymentsBasketAddDiscount, +} from "@/services/ecommerce/payments/hooks"; +import { UseQueryResult } from "@tanstack/react-query"; +import { BasketWithProduct } from "@/services/ecommerce/generated/v0"; + +type CartSummaryProps = { + cartId: number; +}; + +type CartSummaryDiscountProps = { + systemSlug: string; +}; + +const CartSummaryContainer = styled.div(() => ({ + width: "488px", + padding: "0", + ["> div"]: { + padding: "32px", + }, +})); + +const CartSummaryReceiptContainer = styled.div(() => ({ + borderBottom: "1px solid #DDE1E6", + padding: "8px 0", + margin: "8px 0", + width: "100%", +})); + +const CartSummaryTotalContainer = styled.div` + ${({ theme }) => ({ ...theme.typography.h5 })}; + margin-bottom: 20px; + margin-top: 8px; +`; + +const CartSummaryActionContainer = styled.div` + margin: 20px 0; +`; + +const CartSummaryTermsContainer = styled.div` + margin: 20px 0; +`; + +const CartSummaryDiscountContainer = styled.div` + margin-top: 20px; + display: flex; + align-items: start; + justify-content: space-between; +`; + +const ApplyButton = styled(Button)` + margin-top: 20px; +`; + +const CartSummaryDiscount: React.FC = ({ + systemSlug, +}) => { + const discountMutation = usePaymentsBasketAddDiscount(); + const [discountCode, setDiscountCode] = React.useState(""); + + const hndUpdateCode = (ev: React.ChangeEvent) => { + setDiscountCode(ev.target.value); + }; + + const hndApplyDiscount = () => { + discountMutation.mutate({ + system_slug: systemSlug, + discount_code: discountCode, + }); + }; + + return ( + + + + Apply + + + ); +}; + +const CartSummary: React.FC = (props) => { + const { cartId } = props; + const basket = usePaymentsBasketRetrieve( + cartId, + ) as UseQueryResult; + + return ( + basket.data && ( + + + + Order Summary + + + {basket.data.basket_items.map((item) => ( + + ))} + + {basket.data.tax_rate && ( + + )} + + + + + Total + + {basket.data.total_price.toLocaleString("en-US", { + style: "currency", + currency: "USD", + })} + + + + + {basket.data.integrated_system.slug && ( + + )} + + + {basket.data.integrated_system.slug && ( + + )} + + + + By placing my order, I agree to the Terms of Service and Privacy + Policy. + + + + + ) + ); +}; + +export default CartSummary; diff --git a/src/page-components/CartSummaryItem/CartSummaryItem.tsx b/src/page-components/CartSummaryItem/CartSummaryItem.tsx new file mode 100644 index 0000000..7b318df --- /dev/null +++ b/src/page-components/CartSummaryItem/CartSummaryItem.tsx @@ -0,0 +1,103 @@ +import React from "react"; +import { styled } from "@mitodl/smoot-design"; + +import { + BasketItemWithProduct, + BasketWithProduct, +} from "@/services/ecommerce/generated/v0"; + +type CartSummaryItemVariant = "tax" | "item"; +type CartSummaryItemTitleVariant = "" | "discount"; + +type CartSummaryItemProps = { + item: BasketItemWithProduct | BasketWithProduct; + variant?: CartSummaryItemVariant; +}; + +type CartSummaryItemContainerProps = { + variant?: CartSummaryItemVariant; +}; + +type CartSummaryItemTitleProps = { + variant?: CartSummaryItemTitleVariant; +}; + +const CartSummaryItemContainer = styled.div( + ({ variant }) => ({ + display: "flex", + marginTop: variant === "tax" ? "16px" : "", + marginBottom: "8px", + }), +); + +const CartSummaryItemTitle = styled.div( + ({ variant }) => ({ + width: "280px", + marginRight: "16px", + fontStyle: variant === "discount" ? "italic" : "", + }), +); + +const CartSummaryItemValue = styled.div` + width: auto; + flex-grow: 1; + text-align: right; +`; + +const isBasketWithProduct = ( + item: BasketItemWithProduct | BasketWithProduct, +): item is BasketWithProduct => { + return (item as BasketWithProduct).tax !== undefined; +}; + +const CartSummaryItem: React.FC = ({ item, variant }) => { + return ( + <> + {isBasketWithProduct(item) && ( + + + {item.tax_rate.tax_rate_name} + + + {item.tax.toLocaleString("en-US", { + style: "currency", + currency: "USD", + })} + + + )} + {!isBasketWithProduct(item) && ( + <> + + {item.product.name} + + {item.discounted_price.toLocaleString("en-US", { + style: "currency", + currency: "USD", + })} + + + {item.discount_applied && ( + + + Discount Applied: {item.discount_applied.discount_code} + + + - + {( + parseFloat(item.product.price) - item.discounted_price + ).toLocaleString("en-US", { + style: "currency", + currency: "USD", + })} + + + )} + + )} + + ); +}; + +export default CartSummaryItem; +export { CartSummaryItemContainer, CartSummaryItemTitle, CartSummaryItemValue }; diff --git a/src/page-components/EnsureSession/EnsureSession.test.tsx b/src/page-components/EnsureSession/EnsureSession.test.tsx index 0550624..be7babe 100644 --- a/src/page-components/EnsureSession/EnsureSession.test.tsx +++ b/src/page-components/EnsureSession/EnsureSession.test.tsx @@ -30,9 +30,16 @@ describe("EnsureSession", () => { test("Unauthentiated users are redirected", async () => { window.location = { ...window.location, - href: `${window.location.origin}/some/path?cat=meow&dog=woof`, + search: "?system=test-system&cat=meow&dog=woof", + href: "http://test.local:3000/?system=test-system&cat=meow&dog=woof", }; + setMockResponse.get(urls.userMe.get(), { id: null }); + + expect(window.location.search).toBe( + "?system=test-system&cat=meow&dog=woof", + ); + renderWithProviders(); const mockAssign = jest.mocked(window.location.assign); @@ -41,7 +48,7 @@ describe("EnsureSession", () => { }); const url = new URL(mockAssign.mock.calls[0][0]); - const expectedNext = encodeURIComponent(window.location.href); + const expectedNext = encodeURIComponent("test-system"); const expectedHref = `${process.env.NEXT_PUBLIC_UE_API_BASE_URL}/establish_session/?next=${expectedNext}`; expect(url.href).toBe(expectedHref); }); diff --git a/src/page-components/EnsureSession/EnsureSession.tsx b/src/page-components/EnsureSession/EnsureSession.tsx index d9f63a4..6330e19 100644 --- a/src/page-components/EnsureSession/EnsureSession.tsx +++ b/src/page-components/EnsureSession/EnsureSession.tsx @@ -5,17 +5,24 @@ import { useEffect } from "react"; import MuiBackdrop from "@mui/material/Backdrop"; import CircularProgress from "@mui/material/CircularProgress"; import { styled } from "@mitodl/smoot-design"; +import { getCurrentSystem } from "@/utils/system"; const Backdrop = styled(MuiBackdrop)(({ theme }) => ({ zIndex: theme.zIndex.drawer + 1, color: theme.custom.colors.white, })); +const ForceReauthContainer = styled.div` + padding: 10px; + width: 100%; + border-bottom: 1px solid #333; + background-color: rgba(0, 0, 0, 0.1); +`; + const establishSession = () => { - const encoded = encodeURIComponent(window.location.href); - window.location.assign( - `${process.env.NEXT_PUBLIC_UE_API_BASE_URL}/establish_session/?next=${encoded}`, - ); + const urlParams = new URLSearchParams(window.location.search); + const assignUrl = `${process.env.NEXT_PUBLIC_UE_API_BASE_URL}/establish_session/?next=${getCurrentSystem(urlParams)}`; + window.location.assign(assignUrl); }; const EnsureSession = () => { @@ -30,7 +37,16 @@ const EnsureSession = () => { } }, [shouldAuthenticate]); - return isAuthenticated ? null : ( + return isAuthenticated ? ( + +

+ Force reauth:{" "} + {" "} +

+
+ ) : ( diff --git a/src/page-components/PlaceOrderButton/PlaceOrderButton.tsx b/src/page-components/PlaceOrderButton/PlaceOrderButton.tsx new file mode 100644 index 0000000..9cfe1db --- /dev/null +++ b/src/page-components/PlaceOrderButton/PlaceOrderButton.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { Button, styled } from "@mitodl/smoot-design"; +import { usePaymentsCheckoutStartCheckout } from "@/services/ecommerce/payments/hooks"; + +type PlaceOrderButtonProps = { + systemSlug: string; +}; + +const CartPayButton = styled(Button)` + width: 100%; +`; + +const PlaceOrderButton: React.FC = ({ systemSlug }) => { + const checkoutMutation = usePaymentsCheckoutStartCheckout(); + + const handleClick = async () => { + const checkout = await checkoutMutation.mutateAsync({ + system_slug: systemSlug, + }); + + // Construct the form based on the data we got back, then submit it. + + console.log("checkout", checkout); + + const form = document.createElement("form"); + form.method = "POST"; + form.action = checkout.url; + + Object.getOwnPropertyNames(checkout.payload).forEach((propName: string) => { + const input = document.createElement("input"); + input.type = "hidden"; + input.name = propName; + input.value = checkout.payload[propName]; + + form.appendChild(input); + }); + + document.body.appendChild(form); + console.log(form); + form.submit(); + }; + + return Place Order; +}; + +export default PlaceOrderButton; diff --git a/src/services/ecommerce/client.ts b/src/services/ecommerce/client.ts index 1216f5a..51e1082 100644 --- a/src/services/ecommerce/client.ts +++ b/src/services/ecommerce/client.ts @@ -1,5 +1,5 @@ import invariant from "tiny-invariant"; -import { PaymentsApi, UsersApi } from "./generated/v0/api"; +import { PaymentsApi, UsersApi, MetaApi } from "./generated/v0/api"; import axios from "axios"; const BASE_PATH = process.env.NEXT_PUBLIC_UE_API_BASE_URL; @@ -7,12 +7,17 @@ invariant(BASE_PATH, "NEXT_PUBLIC_UE_API_BASE_URL is required."); const instance = axios.create({ withCredentials: true, + withXSRFToken: true, + xsrfCookieName: "csrftoken", + xsrfHeaderName: "X-CSRFTOKEN", }); const paymentsApi = new PaymentsApi(undefined, BASE_PATH, instance); const usersApi = new UsersApi(undefined, BASE_PATH, instance); +const metaApi = new MetaApi(undefined, BASE_PATH, instance); + const devSameSiteCheck = () => { if (process.env.NODE_ENV === "development") { const getSite = (hostname: string) => { @@ -41,4 +46,4 @@ const devSameSiteCheck = () => { } }; -export { paymentsApi, usersApi, BASE_PATH, devSameSiteCheck }; +export { paymentsApi, usersApi, metaApi, BASE_PATH, devSameSiteCheck }; diff --git a/src/services/ecommerce/generated/v0/api.ts b/src/services/ecommerce/generated/v0/api.ts index 68d0047..30b3e18 100644 --- a/src/services/ecommerce/generated/v0/api.ts +++ b/src/services/ecommerce/generated/v0/api.ts @@ -45,12 +45,6 @@ import { * @interface BasketItemWithProduct */ export interface BasketItemWithProduct { - /** - * - * @type {Nested} - * @memberof BasketItemWithProduct - */ - basket: Nested; /** * * @type {Product} @@ -75,6 +69,18 @@ export interface BasketItemWithProduct { * @memberof BasketItemWithProduct */ discounted_price: number; + /** + * + * @type {number} + * @memberof BasketItemWithProduct + */ + quantity?: number; + /** + * + * @type {SimpleDiscount} + * @memberof BasketItemWithProduct + */ + discount_applied: SimpleDiscount; } /** * Basket model serializer with items and products @@ -94,12 +100,36 @@ export interface BasketWithProduct { * @memberof BasketWithProduct */ user: number; + /** + * + * @type {IntegratedSystem} + * @memberof BasketWithProduct + */ + integrated_system: IntegratedSystem; /** * * @type {Array} * @memberof BasketWithProduct */ basket_items: Array; + /** + * Get the subtotal for the basket + * @type {number} + * @memberof BasketWithProduct + */ + subtotal: number; + /** + * Get the tax for the basket + * @type {number} + * @memberof BasketWithProduct + */ + tax: number; + /** + * + * @type {TaxRate} + * @memberof BasketWithProduct + */ + tax_rate: TaxRate; /** * Get the total price for the basket * @type {number} @@ -108,183 +138,1527 @@ export interface BasketWithProduct { total_price: number; } /** - * Serializer for IntegratedSystem model. + * Serializer for companies. * @export - * @interface IntegratedSystem + * @interface Company */ -export interface IntegratedSystem { +export interface Company { /** * * @type {number} - * @memberof IntegratedSystem + * @memberof Company */ id: number; /** * * @type {string} - * @memberof IntegratedSystem + * @memberof Company */ name: string; - /** - * - * @type {string} - * @memberof IntegratedSystem - */ - slug?: string | null; - /** - * - * @type {string} - * @memberof IntegratedSystem - */ - description?: string; } /** - * Serializer for IntegratedSystem model. + * * `AF` - Afghanistan * `AX` - Åland Islands * `AL` - Albania * `DZ` - Algeria * `AS` - American Samoa * `AD` - Andorra * `AO` - Angola * `AI` - Anguilla * `AQ` - Antarctica * `AG` - Antigua and Barbuda * `AR` - Argentina * `AM` - Armenia * `AW` - Aruba * `AU` - Australia * `AT` - Austria * `AZ` - Azerbaijan * `BS` - Bahamas * `BH` - Bahrain * `BD` - Bangladesh * `BB` - Barbados * `BY` - Belarus * `BE` - Belgium * `BZ` - Belize * `BJ` - Benin * `BM` - Bermuda * `BT` - Bhutan * `BO` - Bolivia * `BQ` - Bonaire, Sint Eustatius and Saba * `BA` - Bosnia and Herzegovina * `BW` - Botswana * `BV` - Bouvet Island * `BR` - Brazil * `IO` - British Indian Ocean Territory * `BN` - Brunei * `BG` - Bulgaria * `BF` - Burkina Faso * `BI` - Burundi * `CV` - Cabo Verde * `KH` - Cambodia * `CM` - Cameroon * `CA` - Canada * `KY` - Cayman Islands * `CF` - Central African Republic * `TD` - Chad * `CL` - Chile * `CN` - China * `CX` - Christmas Island * `CC` - Cocos (Keeling) Islands * `CO` - Colombia * `KM` - Comoros * `CG` - Congo * `CD` - Congo (the Democratic Republic of the) * `CK` - Cook Islands * `CR` - Costa Rica * `CI` - Côte d\'Ivoire * `HR` - Croatia * `CU` - Cuba * `CW` - Curaçao * `CY` - Cyprus * `CZ` - Czechia * `DK` - Denmark * `DJ` - Djibouti * `DM` - Dominica * `DO` - Dominican Republic * `EC` - Ecuador * `EG` - Egypt * `SV` - El Salvador * `GQ` - Equatorial Guinea * `ER` - Eritrea * `EE` - Estonia * `SZ` - Eswatini * `ET` - Ethiopia * `FK` - Falkland Islands (Malvinas) * `FO` - Faroe Islands * `FJ` - Fiji * `FI` - Finland * `FR` - France * `GF` - French Guiana * `PF` - French Polynesia * `TF` - French Southern Territories * `GA` - Gabon * `GM` - Gambia * `GE` - Georgia * `DE` - Germany * `GH` - Ghana * `GI` - Gibraltar * `GR` - Greece * `GL` - Greenland * `GD` - Grenada * `GP` - Guadeloupe * `GU` - Guam * `GT` - Guatemala * `GG` - Guernsey * `GN` - Guinea * `GW` - Guinea-Bissau * `GY` - Guyana * `HT` - Haiti * `HM` - Heard Island and McDonald Islands * `VA` - Holy See * `HN` - Honduras * `HK` - Hong Kong * `HU` - Hungary * `IS` - Iceland * `IN` - India * `ID` - Indonesia * `IR` - Iran * `IQ` - Iraq * `IE` - Ireland * `IM` - Isle of Man * `IL` - Israel * `IT` - Italy * `JM` - Jamaica * `JP` - Japan * `JE` - Jersey * `JO` - Jordan * `KZ` - Kazakhstan * `KE` - Kenya * `KI` - Kiribati * `KW` - Kuwait * `KG` - Kyrgyzstan * `LA` - Laos * `LV` - Latvia * `LB` - Lebanon * `LS` - Lesotho * `LR` - Liberia * `LY` - Libya * `LI` - Liechtenstein * `LT` - Lithuania * `LU` - Luxembourg * `MO` - Macao * `MG` - Madagascar * `MW` - Malawi * `MY` - Malaysia * `MV` - Maldives * `ML` - Mali * `MT` - Malta * `MH` - Marshall Islands * `MQ` - Martinique * `MR` - Mauritania * `MU` - Mauritius * `YT` - Mayotte * `MX` - Mexico * `FM` - Micronesia * `MD` - Moldova * `MC` - Monaco * `MN` - Mongolia * `ME` - Montenegro * `MS` - Montserrat * `MA` - Morocco * `MZ` - Mozambique * `MM` - Myanmar * `NA` - Namibia * `NR` - Nauru * `NP` - Nepal * `NL` - Netherlands * `NC` - New Caledonia * `NZ` - New Zealand * `NI` - Nicaragua * `NE` - Niger * `NG` - Nigeria * `NU` - Niue * `NF` - Norfolk Island * `KP` - North Korea * `MK` - North Macedonia * `MP` - Northern Mariana Islands * `NO` - Norway * `OM` - Oman * `PK` - Pakistan * `PW` - Palau * `PS` - Palestine, State of * `PA` - Panama * `PG` - Papua New Guinea * `PY` - Paraguay * `PE` - Peru * `PH` - Philippines * `PN` - Pitcairn * `PL` - Poland * `PT` - Portugal * `PR` - Puerto Rico * `QA` - Qatar * `RE` - Réunion * `RO` - Romania * `RU` - Russia * `RW` - Rwanda * `BL` - Saint Barthélemy * `SH` - Saint Helena, Ascension and Tristan da Cunha * `KN` - Saint Kitts and Nevis * `LC` - Saint Lucia * `MF` - Saint Martin (French part) * `PM` - Saint Pierre and Miquelon * `VC` - Saint Vincent and the Grenadines * `WS` - Samoa * `SM` - San Marino * `ST` - Sao Tome and Principe * `SA` - Saudi Arabia * `SN` - Senegal * `RS` - Serbia * `SC` - Seychelles * `SL` - Sierra Leone * `SG` - Singapore * `SX` - Sint Maarten (Dutch part) * `SK` - Slovakia * `SI` - Slovenia * `SB` - Solomon Islands * `SO` - Somalia * `ZA` - South Africa * `GS` - South Georgia and the South Sandwich Islands * `KR` - South Korea * `SS` - South Sudan * `ES` - Spain * `LK` - Sri Lanka * `SD` - Sudan * `SR` - Suriname * `SJ` - Svalbard and Jan Mayen * `SE` - Sweden * `CH` - Switzerland * `SY` - Syria * `TW` - Taiwan * `TJ` - Tajikistan * `TZ` - Tanzania * `TH` - Thailand * `TL` - Timor-Leste * `TG` - Togo * `TK` - Tokelau * `TO` - Tonga * `TT` - Trinidad and Tobago * `TN` - Tunisia * `TR` - Türkiye * `TM` - Turkmenistan * `TC` - Turks and Caicos Islands * `TV` - Tuvalu * `UG` - Uganda * `UA` - Ukraine * `AE` - United Arab Emirates * `GB` - United Kingdom * `UM` - United States Minor Outlying Islands * `US` - United States of America * `UY` - Uruguay * `UZ` - Uzbekistan * `VU` - Vanuatu * `VE` - Venezuela * `VN` - Vietnam * `VG` - Virgin Islands (British) * `VI` - Virgin Islands (U.S.) * `WF` - Wallis and Futuna * `EH` - Western Sahara * `YE` - Yemen * `ZM` - Zambia * `ZW` - Zimbabwe * @export - * @interface IntegratedSystemRequest + * @enum {string} */ -export interface IntegratedSystemRequest { + +export const CountryCodeEnumDescriptions = { + AF: "Afghanistan", + AX: "Åland Islands", + AL: "Albania", + DZ: "Algeria", + AS: "American Samoa", + AD: "Andorra", + AO: "Angola", + AI: "Anguilla", + AQ: "Antarctica", + AG: "Antigua and Barbuda", + AR: "Argentina", + AM: "Armenia", + AW: "Aruba", + AU: "Australia", + AT: "Austria", + AZ: "Azerbaijan", + BS: "Bahamas", + BH: "Bahrain", + BD: "Bangladesh", + BB: "Barbados", + BY: "Belarus", + BE: "Belgium", + BZ: "Belize", + BJ: "Benin", + BM: "Bermuda", + BT: "Bhutan", + BO: "Bolivia", + BQ: "Bonaire, Sint Eustatius and Saba", + BA: "Bosnia and Herzegovina", + BW: "Botswana", + BV: "Bouvet Island", + BR: "Brazil", + IO: "British Indian Ocean Territory", + BN: "Brunei", + BG: "Bulgaria", + BF: "Burkina Faso", + BI: "Burundi", + CV: "Cabo Verde", + KH: "Cambodia", + CM: "Cameroon", + CA: "Canada", + KY: "Cayman Islands", + CF: "Central African Republic", + TD: "Chad", + CL: "Chile", + CN: "China", + CX: "Christmas Island", + CC: "Cocos (Keeling) Islands", + CO: "Colombia", + KM: "Comoros", + CG: "Congo", + CD: "Congo (the Democratic Republic of the)", + CK: "Cook Islands", + CR: "Costa Rica", + CI: "Côte d'Ivoire", + HR: "Croatia", + CU: "Cuba", + CW: "Curaçao", + CY: "Cyprus", + CZ: "Czechia", + DK: "Denmark", + DJ: "Djibouti", + DM: "Dominica", + DO: "Dominican Republic", + EC: "Ecuador", + EG: "Egypt", + SV: "El Salvador", + GQ: "Equatorial Guinea", + ER: "Eritrea", + EE: "Estonia", + SZ: "Eswatini", + ET: "Ethiopia", + FK: "Falkland Islands (Malvinas)", + FO: "Faroe Islands", + FJ: "Fiji", + FI: "Finland", + FR: "France", + GF: "French Guiana", + PF: "French Polynesia", + TF: "French Southern Territories", + GA: "Gabon", + GM: "Gambia", + GE: "Georgia", + DE: "Germany", + GH: "Ghana", + GI: "Gibraltar", + GR: "Greece", + GL: "Greenland", + GD: "Grenada", + GP: "Guadeloupe", + GU: "Guam", + GT: "Guatemala", + GG: "Guernsey", + GN: "Guinea", + GW: "Guinea-Bissau", + GY: "Guyana", + HT: "Haiti", + HM: "Heard Island and McDonald Islands", + VA: "Holy See", + HN: "Honduras", + HK: "Hong Kong", + HU: "Hungary", + IS: "Iceland", + IN: "India", + ID: "Indonesia", + IR: "Iran", + IQ: "Iraq", + IE: "Ireland", + IM: "Isle of Man", + IL: "Israel", + IT: "Italy", + JM: "Jamaica", + JP: "Japan", + JE: "Jersey", + JO: "Jordan", + KZ: "Kazakhstan", + KE: "Kenya", + KI: "Kiribati", + KW: "Kuwait", + KG: "Kyrgyzstan", + LA: "Laos", + LV: "Latvia", + LB: "Lebanon", + LS: "Lesotho", + LR: "Liberia", + LY: "Libya", + LI: "Liechtenstein", + LT: "Lithuania", + LU: "Luxembourg", + MO: "Macao", + MG: "Madagascar", + MW: "Malawi", + MY: "Malaysia", + MV: "Maldives", + ML: "Mali", + MT: "Malta", + MH: "Marshall Islands", + MQ: "Martinique", + MR: "Mauritania", + MU: "Mauritius", + YT: "Mayotte", + MX: "Mexico", + FM: "Micronesia", + MD: "Moldova", + MC: "Monaco", + MN: "Mongolia", + ME: "Montenegro", + MS: "Montserrat", + MA: "Morocco", + MZ: "Mozambique", + MM: "Myanmar", + NA: "Namibia", + NR: "Nauru", + NP: "Nepal", + NL: "Netherlands", + NC: "New Caledonia", + NZ: "New Zealand", + NI: "Nicaragua", + NE: "Niger", + NG: "Nigeria", + NU: "Niue", + NF: "Norfolk Island", + KP: "North Korea", + MK: "North Macedonia", + MP: "Northern Mariana Islands", + NO: "Norway", + OM: "Oman", + PK: "Pakistan", + PW: "Palau", + PS: "Palestine, State of", + PA: "Panama", + PG: "Papua New Guinea", + PY: "Paraguay", + PE: "Peru", + PH: "Philippines", + PN: "Pitcairn", + PL: "Poland", + PT: "Portugal", + PR: "Puerto Rico", + QA: "Qatar", + RE: "Réunion", + RO: "Romania", + RU: "Russia", + RW: "Rwanda", + BL: "Saint Barthélemy", + SH: "Saint Helena, Ascension and Tristan da Cunha", + KN: "Saint Kitts and Nevis", + LC: "Saint Lucia", + MF: "Saint Martin (French part)", + PM: "Saint Pierre and Miquelon", + VC: "Saint Vincent and the Grenadines", + WS: "Samoa", + SM: "San Marino", + ST: "Sao Tome and Principe", + SA: "Saudi Arabia", + SN: "Senegal", + RS: "Serbia", + SC: "Seychelles", + SL: "Sierra Leone", + SG: "Singapore", + SX: "Sint Maarten (Dutch part)", + SK: "Slovakia", + SI: "Slovenia", + SB: "Solomon Islands", + SO: "Somalia", + ZA: "South Africa", + GS: "South Georgia and the South Sandwich Islands", + KR: "South Korea", + SS: "South Sudan", + ES: "Spain", + LK: "Sri Lanka", + SD: "Sudan", + SR: "Suriname", + SJ: "Svalbard and Jan Mayen", + SE: "Sweden", + CH: "Switzerland", + SY: "Syria", + TW: "Taiwan", + TJ: "Tajikistan", + TZ: "Tanzania", + TH: "Thailand", + TL: "Timor-Leste", + TG: "Togo", + TK: "Tokelau", + TO: "Tonga", + TT: "Trinidad and Tobago", + TN: "Tunisia", + TR: "Türkiye", + TM: "Turkmenistan", + TC: "Turks and Caicos Islands", + TV: "Tuvalu", + UG: "Uganda", + UA: "Ukraine", + AE: "United Arab Emirates", + GB: "United Kingdom", + UM: "United States Minor Outlying Islands", + US: "United States of America", + UY: "Uruguay", + UZ: "Uzbekistan", + VU: "Vanuatu", + VE: "Venezuela", + VN: "Vietnam", + VG: "Virgin Islands (British)", + VI: "Virgin Islands (U.S.)", + WF: "Wallis and Futuna", + EH: "Western Sahara", + YE: "Yemen", + ZM: "Zambia", + ZW: "Zimbabwe", +} as const; + +export const CountryCodeEnum = { /** - * - * @type {string} - * @memberof IntegratedSystemRequest + * Afghanistan */ - name: string; + Af: "AF", /** - * - * @type {string} - * @memberof IntegratedSystemRequest + * Åland Islands */ - slug?: string | null; + Ax: "AX", /** - * - * @type {string} - * @memberof IntegratedSystemRequest + * Albania */ - description?: string; -} -/** - * Serializes a line item for an order. - * @export - * @interface Line - */ -export interface Line { + Al: "AL", /** - * - * @type {number} - * @memberof Line + * Algeria */ - id: number; + Dz: "DZ", /** - * - * @type {number} - * @memberof Line + * American Samoa */ - quantity: number; + As: "AS", /** - * Return the item description - * @type {string} - * @memberof Line + * Andorra */ - item_description: string; + Ad: "AD", /** - * - * @type {string} - * @memberof Line + * Angola */ - unit_price: string; + Ao: "AO", /** - * - * @type {string} - * @memberof Line + * Anguilla */ - total_price: string; + Ai: "AI", /** - * - * @type {Product} - * @memberof Line + * Antarctica */ - product: Product; -} -/** - * - * @export - * @interface Nested - */ -export interface Nested { + Aq: "AQ", /** - * - * @type {number} - * @memberof Nested + * Antigua and Barbuda */ - id: number; + Ag: "AG", /** - * - * @type {string} - * @memberof Nested + * Argentina */ - created_on: string; + Ar: "AR", /** - * - * @type {string} - * @memberof Nested + * Armenia + */ + Am: "AM", + /** + * Aruba + */ + Aw: "AW", + /** + * Australia + */ + Au: "AU", + /** + * Austria + */ + At: "AT", + /** + * Azerbaijan + */ + Az: "AZ", + /** + * Bahamas + */ + Bs: "BS", + /** + * Bahrain + */ + Bh: "BH", + /** + * Bangladesh + */ + Bd: "BD", + /** + * Barbados + */ + Bb: "BB", + /** + * Belarus + */ + By: "BY", + /** + * Belgium + */ + Be: "BE", + /** + * Belize + */ + Bz: "BZ", + /** + * Benin + */ + Bj: "BJ", + /** + * Bermuda + */ + Bm: "BM", + /** + * Bhutan + */ + Bt: "BT", + /** + * Bolivia + */ + Bo: "BO", + /** + * Bonaire, Sint Eustatius and Saba + */ + Bq: "BQ", + /** + * Bosnia and Herzegovina + */ + Ba: "BA", + /** + * Botswana + */ + Bw: "BW", + /** + * Bouvet Island + */ + Bv: "BV", + /** + * Brazil + */ + Br: "BR", + /** + * British Indian Ocean Territory + */ + Io: "IO", + /** + * Brunei + */ + Bn: "BN", + /** + * Bulgaria + */ + Bg: "BG", + /** + * Burkina Faso + */ + Bf: "BF", + /** + * Burundi + */ + Bi: "BI", + /** + * Cabo Verde + */ + Cv: "CV", + /** + * Cambodia + */ + Kh: "KH", + /** + * Cameroon + */ + Cm: "CM", + /** + * Canada + */ + Ca: "CA", + /** + * Cayman Islands + */ + Ky: "KY", + /** + * Central African Republic + */ + Cf: "CF", + /** + * Chad + */ + Td: "TD", + /** + * Chile + */ + Cl: "CL", + /** + * China + */ + Cn: "CN", + /** + * Christmas Island + */ + Cx: "CX", + /** + * Cocos (Keeling) Islands + */ + Cc: "CC", + /** + * Colombia + */ + Co: "CO", + /** + * Comoros + */ + Km: "KM", + /** + * Congo + */ + Cg: "CG", + /** + * Congo (the Democratic Republic of the) + */ + Cd: "CD", + /** + * Cook Islands + */ + Ck: "CK", + /** + * Costa Rica + */ + Cr: "CR", + /** + * Côte d'Ivoire + */ + Ci: "CI", + /** + * Croatia + */ + Hr: "HR", + /** + * Cuba + */ + Cu: "CU", + /** + * Curaçao + */ + Cw: "CW", + /** + * Cyprus + */ + Cy: "CY", + /** + * Czechia + */ + Cz: "CZ", + /** + * Denmark + */ + Dk: "DK", + /** + * Djibouti + */ + Dj: "DJ", + /** + * Dominica + */ + Dm: "DM", + /** + * Dominican Republic + */ + Do: "DO", + /** + * Ecuador + */ + Ec: "EC", + /** + * Egypt + */ + Eg: "EG", + /** + * El Salvador + */ + Sv: "SV", + /** + * Equatorial Guinea + */ + Gq: "GQ", + /** + * Eritrea + */ + Er: "ER", + /** + * Estonia + */ + Ee: "EE", + /** + * Eswatini + */ + Sz: "SZ", + /** + * Ethiopia + */ + Et: "ET", + /** + * Falkland Islands (Malvinas) + */ + Fk: "FK", + /** + * Faroe Islands + */ + Fo: "FO", + /** + * Fiji + */ + Fj: "FJ", + /** + * Finland + */ + Fi: "FI", + /** + * France + */ + Fr: "FR", + /** + * French Guiana + */ + Gf: "GF", + /** + * French Polynesia + */ + Pf: "PF", + /** + * French Southern Territories + */ + Tf: "TF", + /** + * Gabon + */ + Ga: "GA", + /** + * Gambia + */ + Gm: "GM", + /** + * Georgia + */ + Ge: "GE", + /** + * Germany + */ + De: "DE", + /** + * Ghana + */ + Gh: "GH", + /** + * Gibraltar + */ + Gi: "GI", + /** + * Greece + */ + Gr: "GR", + /** + * Greenland + */ + Gl: "GL", + /** + * Grenada + */ + Gd: "GD", + /** + * Guadeloupe + */ + Gp: "GP", + /** + * Guam + */ + Gu: "GU", + /** + * Guatemala + */ + Gt: "GT", + /** + * Guernsey + */ + Gg: "GG", + /** + * Guinea + */ + Gn: "GN", + /** + * Guinea-Bissau + */ + Gw: "GW", + /** + * Guyana + */ + Gy: "GY", + /** + * Haiti + */ + Ht: "HT", + /** + * Heard Island and McDonald Islands + */ + Hm: "HM", + /** + * Holy See + */ + Va: "VA", + /** + * Honduras + */ + Hn: "HN", + /** + * Hong Kong + */ + Hk: "HK", + /** + * Hungary + */ + Hu: "HU", + /** + * Iceland + */ + Is: "IS", + /** + * India + */ + In: "IN", + /** + * Indonesia + */ + Id: "ID", + /** + * Iran + */ + Ir: "IR", + /** + * Iraq + */ + Iq: "IQ", + /** + * Ireland + */ + Ie: "IE", + /** + * Isle of Man + */ + Im: "IM", + /** + * Israel + */ + Il: "IL", + /** + * Italy + */ + It: "IT", + /** + * Jamaica + */ + Jm: "JM", + /** + * Japan + */ + Jp: "JP", + /** + * Jersey + */ + Je: "JE", + /** + * Jordan + */ + Jo: "JO", + /** + * Kazakhstan + */ + Kz: "KZ", + /** + * Kenya + */ + Ke: "KE", + /** + * Kiribati + */ + Ki: "KI", + /** + * Kuwait + */ + Kw: "KW", + /** + * Kyrgyzstan + */ + Kg: "KG", + /** + * Laos + */ + La: "LA", + /** + * Latvia + */ + Lv: "LV", + /** + * Lebanon + */ + Lb: "LB", + /** + * Lesotho + */ + Ls: "LS", + /** + * Liberia + */ + Lr: "LR", + /** + * Libya + */ + Ly: "LY", + /** + * Liechtenstein + */ + Li: "LI", + /** + * Lithuania + */ + Lt: "LT", + /** + * Luxembourg + */ + Lu: "LU", + /** + * Macao + */ + Mo: "MO", + /** + * Madagascar + */ + Mg: "MG", + /** + * Malawi + */ + Mw: "MW", + /** + * Malaysia + */ + My: "MY", + /** + * Maldives + */ + Mv: "MV", + /** + * Mali + */ + Ml: "ML", + /** + * Malta + */ + Mt: "MT", + /** + * Marshall Islands + */ + Mh: "MH", + /** + * Martinique + */ + Mq: "MQ", + /** + * Mauritania + */ + Mr: "MR", + /** + * Mauritius + */ + Mu: "MU", + /** + * Mayotte + */ + Yt: "YT", + /** + * Mexico + */ + Mx: "MX", + /** + * Micronesia + */ + Fm: "FM", + /** + * Moldova + */ + Md: "MD", + /** + * Monaco + */ + Mc: "MC", + /** + * Mongolia + */ + Mn: "MN", + /** + * Montenegro + */ + Me: "ME", + /** + * Montserrat + */ + Ms: "MS", + /** + * Morocco + */ + Ma: "MA", + /** + * Mozambique + */ + Mz: "MZ", + /** + * Myanmar + */ + Mm: "MM", + /** + * Namibia + */ + Na: "NA", + /** + * Nauru + */ + Nr: "NR", + /** + * Nepal + */ + Np: "NP", + /** + * Netherlands + */ + Nl: "NL", + /** + * New Caledonia + */ + Nc: "NC", + /** + * New Zealand + */ + Nz: "NZ", + /** + * Nicaragua + */ + Ni: "NI", + /** + * Niger + */ + Ne: "NE", + /** + * Nigeria + */ + Ng: "NG", + /** + * Niue + */ + Nu: "NU", + /** + * Norfolk Island + */ + Nf: "NF", + /** + * North Korea + */ + Kp: "KP", + /** + * North Macedonia + */ + Mk: "MK", + /** + * Northern Mariana Islands + */ + Mp: "MP", + /** + * Norway + */ + No: "NO", + /** + * Oman + */ + Om: "OM", + /** + * Pakistan + */ + Pk: "PK", + /** + * Palau + */ + Pw: "PW", + /** + * Palestine, State of + */ + Ps: "PS", + /** + * Panama + */ + Pa: "PA", + /** + * Papua New Guinea + */ + Pg: "PG", + /** + * Paraguay + */ + Py: "PY", + /** + * Peru + */ + Pe: "PE", + /** + * Philippines + */ + Ph: "PH", + /** + * Pitcairn + */ + Pn: "PN", + /** + * Poland + */ + Pl: "PL", + /** + * Portugal + */ + Pt: "PT", + /** + * Puerto Rico + */ + Pr: "PR", + /** + * Qatar + */ + Qa: "QA", + /** + * Réunion + */ + Re: "RE", + /** + * Romania + */ + Ro: "RO", + /** + * Russia + */ + Ru: "RU", + /** + * Rwanda + */ + Rw: "RW", + /** + * Saint Barthélemy + */ + Bl: "BL", + /** + * Saint Helena, Ascension and Tristan da Cunha + */ + Sh: "SH", + /** + * Saint Kitts and Nevis + */ + Kn: "KN", + /** + * Saint Lucia + */ + Lc: "LC", + /** + * Saint Martin (French part) + */ + Mf: "MF", + /** + * Saint Pierre and Miquelon + */ + Pm: "PM", + /** + * Saint Vincent and the Grenadines + */ + Vc: "VC", + /** + * Samoa + */ + Ws: "WS", + /** + * San Marino + */ + Sm: "SM", + /** + * Sao Tome and Principe + */ + St: "ST", + /** + * Saudi Arabia + */ + Sa: "SA", + /** + * Senegal + */ + Sn: "SN", + /** + * Serbia + */ + Rs: "RS", + /** + * Seychelles + */ + Sc: "SC", + /** + * Sierra Leone + */ + Sl: "SL", + /** + * Singapore + */ + Sg: "SG", + /** + * Sint Maarten (Dutch part) + */ + Sx: "SX", + /** + * Slovakia + */ + Sk: "SK", + /** + * Slovenia + */ + Si: "SI", + /** + * Solomon Islands + */ + Sb: "SB", + /** + * Somalia + */ + So: "SO", + /** + * South Africa + */ + Za: "ZA", + /** + * South Georgia and the South Sandwich Islands + */ + Gs: "GS", + /** + * South Korea + */ + Kr: "KR", + /** + * South Sudan + */ + Ss: "SS", + /** + * Spain + */ + Es: "ES", + /** + * Sri Lanka + */ + Lk: "LK", + /** + * Sudan + */ + Sd: "SD", + /** + * Suriname + */ + Sr: "SR", + /** + * Svalbard and Jan Mayen + */ + Sj: "SJ", + /** + * Sweden + */ + Se: "SE", + /** + * Switzerland + */ + Ch: "CH", + /** + * Syria + */ + Sy: "SY", + /** + * Taiwan + */ + Tw: "TW", + /** + * Tajikistan + */ + Tj: "TJ", + /** + * Tanzania + */ + Tz: "TZ", + /** + * Thailand + */ + Th: "TH", + /** + * Timor-Leste + */ + Tl: "TL", + /** + * Togo + */ + Tg: "TG", + /** + * Tokelau + */ + Tk: "TK", + /** + * Tonga + */ + To: "TO", + /** + * Trinidad and Tobago + */ + Tt: "TT", + /** + * Tunisia + */ + Tn: "TN", + /** + * Türkiye + */ + Tr: "TR", + /** + * Turkmenistan + */ + Tm: "TM", + /** + * Turks and Caicos Islands + */ + Tc: "TC", + /** + * Tuvalu + */ + Tv: "TV", + /** + * Uganda + */ + Ug: "UG", + /** + * Ukraine + */ + Ua: "UA", + /** + * United Arab Emirates + */ + Ae: "AE", + /** + * United Kingdom + */ + Gb: "GB", + /** + * United States Minor Outlying Islands + */ + Um: "UM", + /** + * United States of America + */ + Us: "US", + /** + * Uruguay + */ + Uy: "UY", + /** + * Uzbekistan + */ + Uz: "UZ", + /** + * Vanuatu + */ + Vu: "VU", + /** + * Venezuela + */ + Ve: "VE", + /** + * Vietnam + */ + Vn: "VN", + /** + * Virgin Islands (British) + */ + Vg: "VG", + /** + * Virgin Islands (U.S.) + */ + Vi: "VI", + /** + * Wallis and Futuna + */ + Wf: "WF", + /** + * Western Sahara + */ + Eh: "EH", + /** + * Yemen + */ + Ye: "YE", + /** + * Zambia + */ + Zm: "ZM", + /** + * Zimbabwe + */ + Zw: "ZW", +} as const; + +export type CountryCodeEnum = + (typeof CountryCodeEnum)[keyof typeof CountryCodeEnum]; + +/** + * Really basic serializer for the payload that we need to send to CyberSource. + * @export + * @interface CyberSourceCheckout + */ +export interface CyberSourceCheckout { + /** + * + * @type {{ [key: string]: any; }} + * @memberof CyberSourceCheckout + */ + payload: { [key: string]: any }; + /** + * + * @type {string} + * @memberof CyberSourceCheckout + */ + url: string; + /** + * + * @type {string} + * @memberof CyberSourceCheckout + */ + method: string; +} +/** + * Serializer for discounts. + * @export + * @interface Discount + */ +export interface Discount { + /** + * + * @type {number} + * @memberof Discount + */ + id: number; + /** + * + * @type {string} + * @memberof Discount + */ + discount_code: string; + /** + * + * @type {string} + * @memberof Discount + */ + amount: string; + /** + * + * @type {PaymentTypeEnum} + * @memberof Discount + */ + payment_type?: PaymentTypeEnum | null; + /** + * + * @type {number} + * @memberof Discount + */ + max_redemptions?: number | null; + /** + * If set, this discount code will not be redeemable before this date. + * @type {string} + * @memberof Discount + */ + activation_date?: string | null; + /** + * If set, this discount code will not be redeemable after this date. + * @type {string} + * @memberof Discount + */ + expiration_date?: string | null; + /** + * + * @type {IntegratedSystem} + * @memberof Discount + */ + integrated_system: IntegratedSystem; + /** + * + * @type {Product} + * @memberof Discount + */ + product: Product; + /** + * + * @type {Array} + * @memberof Discount + */ + assigned_users: Array; + /** + * + * @type {Company} + * @memberof Discount + */ + company: Company; +} + +/** + * * `percent-off` - percent-off * `dollars-off` - dollars-off * `fixed-price` - fixed-price + * @export + * @enum {string} + */ + +export const DiscountTypeEnumDescriptions = { + "percent-off": "percent-off", + "dollars-off": "dollars-off", + "fixed-price": "fixed-price", +} as const; + +export const DiscountTypeEnum = { + /** + * percent-off + */ + PercentOff: "percent-off", + /** + * dollars-off + */ + DollarsOff: "dollars-off", + /** + * fixed-price + */ + FixedPrice: "fixed-price", +} as const; + +export type DiscountTypeEnum = + (typeof DiscountTypeEnum)[keyof typeof DiscountTypeEnum]; + +/** + * Serializer for IntegratedSystem model. + * @export + * @interface IntegratedSystem + */ +export interface IntegratedSystem { + /** + * + * @type {number} + * @memberof IntegratedSystem + */ + id: number; + /** + * + * @type {string} + * @memberof IntegratedSystem + */ + name: string; + /** + * + * @type {string} + * @memberof IntegratedSystem + */ + slug?: string | null; + /** + * + * @type {string} + * @memberof IntegratedSystem + */ + description?: string; +} +/** + * Serializer for IntegratedSystem model. + * @export + * @interface IntegratedSystemRequest + */ +export interface IntegratedSystemRequest { + /** + * + * @type {string} + * @memberof IntegratedSystemRequest */ - updated_on: string; + name: string; /** - * The IP address of the user. + * * @type {string} - * @memberof Nested + * @memberof IntegratedSystemRequest */ - user_ip?: string; + slug?: string | null; /** - * The country code for the user for this basket for tax purposes. + * * @type {string} - * @memberof Nested + * @memberof IntegratedSystemRequest */ - user_taxable_country_code?: string | null; + description?: string; +} +/** + * Serializes a line item for an order. + * @export + * @interface Line + */ +export interface Line { /** * - * @type {UserTaxableGeolocationTypeEnum} - * @memberof Nested + * @type {number} + * @memberof Line */ - user_taxable_geolocation_type?: UserTaxableGeolocationTypeEnum; + id: number; /** - * The country code for the user for this basket for blocked items. - * @type {string} - * @memberof Nested + * + * @type {number} + * @memberof Line */ - user_blockable_country_code?: string | null; + quantity: number; /** - * How the user\'s location was determined for blocked items. + * Return the item description * @type {string} - * @memberof Nested + * @memberof Line */ - user_blockable_geolocation_type?: string; + item_description: string; /** * - * @type {number} - * @memberof Nested + * @type {string} + * @memberof Line */ - user: number; + unit_price: string; /** * - * @type {number} - * @memberof Nested - */ - integrated_system: number; - /** - * The tax rate assessed for this basket. - * @type {number} - * @memberof Nested + * @type {string} + * @memberof Line */ - tax_rate?: number | null; + total_price: string; /** * - * @type {Array} - * @memberof Nested + * @type {Product} + * @memberof Line */ - discounts: Array; + product: Product; } +/** + * + * @export + * @enum {string} + */ + +export const NullEnumDescriptions = { + null: "", +} as const; + +export const NullEnum = { + Null: "null", +} as const; + +export type NullEnum = (typeof NullEnum)[keyof typeof NullEnum]; /** * Serializer for order history. @@ -509,12 +1883,6 @@ export interface PatchedProductRequest { * @memberof PatchedProductRequest */ name?: string; - /** - * Price (decimal to two places) - * @type {string} - * @memberof PatchedProductRequest - */ - price?: string; /** * Long description of the product. * @type {string} @@ -533,43 +1901,80 @@ export interface PatchedProductRequest { * @memberof PatchedProductRequest */ system?: number; + /** + * Price (decimal to two places) + * @type {string} + * @memberof PatchedProductRequest + */ + price?: string; } /** - * Serializer for Product model. + * * `marketing` - marketing * `sales` - sales * `financial-assistance` - financial-assistance * `customer-support` - customer-support * `staff` - staff * `legacy` - legacy * `credit_card` - credit_card * `purchase_order` - purchase_order * @export - * @interface Product + * @enum {string} */ -export interface Product { + +export const PaymentTypeEnumDescriptions = { + marketing: "marketing", + sales: "sales", + "financial-assistance": "financial-assistance", + "customer-support": "customer-support", + staff: "staff", + legacy: "legacy", + credit_card: "credit_card", + purchase_order: "purchase_order", +} as const; + +export const PaymentTypeEnum = { /** - * - * @type {number} - * @memberof Product + * marketing */ - id: number; + Marketing: "marketing", /** - * - * @type {string} - * @memberof Product + * sales */ - deleted_on: string | null; + Sales: "sales", /** - * - * @type {boolean} - * @memberof Product + * financial-assistance */ - deleted_by_cascade: boolean; + FinancialAssistance: "financial-assistance", /** - * - * @type {string} - * @memberof Product + * customer-support */ - created_on: string; + CustomerSupport: "customer-support", + /** + * staff + */ + Staff: "staff", + /** + * legacy + */ + Legacy: "legacy", + /** + * credit_card + */ + CreditCard: "credit_card", + /** + * purchase_order + */ + PurchaseOrder: "purchase_order", +} as const; + +export type PaymentTypeEnum = + (typeof PaymentTypeEnum)[keyof typeof PaymentTypeEnum]; + +/** + * Serializer for Product model. + * @export + * @interface Product + */ +export interface Product { /** * - * @type {string} + * @type {number} * @memberof Product */ - updated_on: string; + id: number; /** * SKU of the product. * @type {string} @@ -582,12 +1987,6 @@ export interface Product { * @memberof Product */ name: string; - /** - * Price (decimal to two places) - * @type {string} - * @memberof Product - */ - price: string; /** * Long description of the product. * @type {string} @@ -606,6 +2005,18 @@ export interface Product { * @memberof Product */ system: number; + /** + * Price (decimal to two places) + * @type {string} + * @memberof Product + */ + price: string; + /** + * + * @type {boolean} + * @memberof Product + */ + deleted_by_cascade: boolean; } /** * Serializer for Product model. @@ -625,12 +2036,6 @@ export interface ProductRequest { * @memberof ProductRequest */ name: string; - /** - * Price (decimal to two places) - * @type {string} - * @memberof ProductRequest - */ - price: string; /** * Long description of the product. * @type {string} @@ -649,7 +2054,51 @@ export interface ProductRequest { * @memberof ProductRequest */ system: number; + /** + * Price (decimal to two places) + * @type {string} + * @memberof ProductRequest + */ + price: string; +} +/** + * Simpler serializer for discounts. + * @export + * @interface SimpleDiscount + */ +export interface SimpleDiscount { + /** + * + * @type {number} + * @memberof SimpleDiscount + */ + id: number; + /** + * + * @type {string} + * @memberof SimpleDiscount + */ + discount_code: string; + /** + * + * @type {string} + * @memberof SimpleDiscount + */ + amount: string; + /** + * + * @type {DiscountTypeEnum} + * @memberof SimpleDiscount + */ + discount_type: DiscountTypeEnum; + /** + * Return the formatted discount amount. This quantizes percent discounts to whole numbers. This is probably fine. + * @type {string} + * @memberof SimpleDiscount + */ + formatted_discount_amount: string; } + /** * * `pending` - Pending * `fulfilled` - Fulfilled * `canceled` - Canceled * `refunded` - Refunded * `declined` - Declined * `errored` - Errored * `review` - Review * @export @@ -699,6 +2148,38 @@ export const StateEnum = { export type StateEnum = (typeof StateEnum)[keyof typeof StateEnum]; +/** + * TaxRate model serializer + * @export + * @interface TaxRate + */ +export interface TaxRate { + /** + * + * @type {number} + * @memberof TaxRate + */ + id: number; + /** + * + * @type {CountryCodeEnum} + * @memberof TaxRate + */ + country_code: CountryCodeEnum; + /** + * + * @type {string} + * @memberof TaxRate + */ + tax_rate?: string; + /** + * + * @type {string} + * @memberof TaxRate + */ + tax_rate_name?: string; +} + /** * Serializer for User model. * @export @@ -736,35 +2217,6 @@ export interface User { */ last_name?: string; } -/** - * * `profile` - profile * `geoip` - geoip * `none` - none - * @export - * @enum {string} - */ - -export const UserTaxableGeolocationTypeEnumDescriptions = { - profile: "profile", - geoip: "geoip", - none: "none", -} as const; - -export const UserTaxableGeolocationTypeEnum = { - /** - * profile - */ - Profile: "profile", - /** - * geoip - */ - Geoip: "geoip", - /** - * none - */ - None: "none", -} as const; - -export type UserTaxableGeolocationTypeEnum = - (typeof UserTaxableGeolocationTypeEnum)[keyof typeof UserTaxableGeolocationTypeEnum]; /** * MetaApi - axios parameter creator @@ -2452,14 +3904,22 @@ export const PaymentsApiAxiosParamCreator = function ( return { /** * Creates or updates a basket for the current user, adding the discount if valid. + * @param {string} discount_code * @param {string} system_slug * @param {*} [options] Override http request option. * @throws {RequiredError} */ paymentsBasketsAddDiscountCreate: async ( + discount_code: string, system_slug: string, options: RawAxiosRequestConfig = {}, ): Promise => { + // verify required parameter 'discount_code' is not null or undefined + assertParamExists( + "paymentsBasketsAddDiscountCreate", + "discount_code", + discount_code, + ); // verify required parameter 'system_slug' is not null or undefined assertParamExists( "paymentsBasketsAddDiscountCreate", @@ -2486,6 +3946,10 @@ export const PaymentsApiAxiosParamCreator = function ( const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; + if (discount_code !== undefined) { + localVarQueryParameter["discount_code"] = discount_code; + } + setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; @@ -2529,7 +3993,63 @@ export const PaymentsApiAxiosParamCreator = function ( } const localVarRequestOptions = { - method: "DELETE", + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Creates or updates a basket for the current user, adding the selected product. + * @param {string} sku + * @param {string} system_slug + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + paymentsBasketsCreateFromProductCreate: async ( + sku: string, + system_slug: string, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'sku' is not null or undefined + assertParamExists("paymentsBasketsCreateFromProductCreate", "sku", sku); + // verify required parameter 'system_slug' is not null or undefined + assertParamExists( + "paymentsBasketsCreateFromProductCreate", + "system_slug", + system_slug, + ); + const localVarPath = + `/api/v0/payments/baskets/create_from_product/{system_slug}/{sku}/` + .replace(`{${"sku"}}`, encodeURIComponent(String(sku))) + .replace( + `{${"system_slug"}}`, + encodeURIComponent(String(system_slug)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", ...baseOptions, ...options, }; @@ -2551,32 +4071,26 @@ export const PaymentsApiAxiosParamCreator = function ( }; }, /** - * Creates or updates a basket for the current user, adding the selected product. - * @param {string} sku + * Returns or creates a basket for the current user and system. * @param {string} system_slug * @param {*} [options] Override http request option. * @throws {RequiredError} */ - paymentsBasketsCreateFromProductCreate: async ( - sku: string, + paymentsBasketsForSystemRetrieve: async ( system_slug: string, options: RawAxiosRequestConfig = {}, ): Promise => { - // verify required parameter 'sku' is not null or undefined - assertParamExists("paymentsBasketsCreateFromProductCreate", "sku", sku); // verify required parameter 'system_slug' is not null or undefined assertParamExists( - "paymentsBasketsCreateFromProductCreate", + "paymentsBasketsForSystemRetrieve", "system_slug", system_slug, ); const localVarPath = - `/api/v0/payments/baskets/create_from_product/{system_slug}/{sku}/` - .replace(`{${"sku"}}`, encodeURIComponent(String(sku))) - .replace( - `{${"system_slug"}}`, - encodeURIComponent(String(system_slug)), - ); + `/api/v0/payments/baskets/for_system/{system_slug}/`.replace( + `{${"system_slug"}}`, + encodeURIComponent(String(system_slug)), + ); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; @@ -2585,7 +4099,7 @@ export const PaymentsApiAxiosParamCreator = function ( } const localVarRequestOptions = { - method: "POST", + method: "GET", ...baseOptions, ...options, }; @@ -2707,6 +4221,89 @@ export const PaymentsApiAxiosParamCreator = function ( options: localVarRequestOptions, }; }, + /** + * Generates and returns the form payload for the current basket for the specified system, which can be used to start the checkout process. + * @param {string} system_slug + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + paymentsCheckoutCreate: async ( + system_slug: string, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'system_slug' is not null or undefined + assertParamExists("paymentsCheckoutCreate", "system_slug", system_slug); + const localVarPath = `/api/v0/payments/checkout/{system_slug}/`.replace( + `{${"system_slug"}}`, + encodeURIComponent(String(system_slug)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Create a discount. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + paymentsDiscountsCreate: async ( + options: RawAxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/api/v0/payments/discounts/`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * Retrives the current user\'s completed orders. * @param {number} [limit] Number of results to return per page. @@ -2814,11 +4411,13 @@ export const PaymentsApiFp = function (configuration?: Configuration) { return { /** * Creates or updates a basket for the current user, adding the discount if valid. + * @param {string} discount_code * @param {string} system_slug * @param {*} [options] Override http request option. * @throws {RequiredError} */ async paymentsBasketsAddDiscountCreate( + discount_code: string, system_slug: string, options?: RawAxiosRequestConfig, ): Promise< @@ -2829,6 +4428,7 @@ export const PaymentsApiFp = function (configuration?: Configuration) { > { const localVarAxiosArgs = await localVarAxiosParamCreator.paymentsBasketsAddDiscountCreate( + discount_code, system_slug, options, ); @@ -2910,6 +4510,39 @@ export const PaymentsApiFp = function (configuration?: Configuration) { configuration, )(axios, operationBasePath || basePath); }, + /** + * Returns or creates a basket for the current user and system. + * @param {string} system_slug + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async paymentsBasketsForSystemRetrieve( + system_slug: string, + options?: RawAxiosRequestConfig, + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string, + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.paymentsBasketsForSystemRetrieve( + system_slug, + options, + ); + const index = configuration?.serverIndex ?? 0; + const operationBasePath = + operationServerMap["PaymentsApi.paymentsBasketsForSystemRetrieve"]?.[ + index + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, operationBasePath || basePath); + }, /** * Retrives the current user\'s baskets, one per system. * @param {number} [integrated_system] @@ -2975,6 +4608,60 @@ export const PaymentsApiFp = function (configuration?: Configuration) { configuration, )(axios, operationBasePath || basePath); }, + /** + * Generates and returns the form payload for the current basket for the specified system, which can be used to start the checkout process. + * @param {string} system_slug + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async paymentsCheckoutCreate( + system_slug: string, + options?: RawAxiosRequestConfig, + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string, + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.paymentsCheckoutCreate( + system_slug, + options, + ); + const index = configuration?.serverIndex ?? 0; + const operationBasePath = + operationServerMap["PaymentsApi.paymentsCheckoutCreate"]?.[index]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, operationBasePath || basePath); + }, + /** + * Create a discount. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async paymentsDiscountsCreate( + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.paymentsDiscountsCreate(options); + const index = configuration?.serverIndex ?? 0; + const operationBasePath = + operationServerMap["PaymentsApi.paymentsDiscountsCreate"]?.[index]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, operationBasePath || basePath); + }, /** * Retrives the current user\'s completed orders. * @param {number} [limit] Number of results to return per page. @@ -3065,6 +4752,7 @@ export const PaymentsApiFactory = function ( ): AxiosPromise { return localVarFp .paymentsBasketsAddDiscountCreate( + requestParameters.discount_code, requestParameters.system_slug, options, ) @@ -3102,6 +4790,23 @@ export const PaymentsApiFactory = function ( ) .then((request) => request(axios, basePath)); }, + /** + * Returns or creates a basket for the current user and system. + * @param {PaymentsApiPaymentsBasketsForSystemRetrieveRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + paymentsBasketsForSystemRetrieve( + requestParameters: PaymentsApiPaymentsBasketsForSystemRetrieveRequest, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .paymentsBasketsForSystemRetrieve( + requestParameters.system_slug, + options, + ) + .then((request) => request(axios, basePath)); + }, /** * Retrives the current user\'s baskets, one per system. * @param {PaymentsApiPaymentsBasketsListRequest} requestParameters Request parameters. @@ -3135,6 +4840,32 @@ export const PaymentsApiFactory = function ( .paymentsBasketsRetrieve(requestParameters.id, options) .then((request) => request(axios, basePath)); }, + /** + * Generates and returns the form payload for the current basket for the specified system, which can be used to start the checkout process. + * @param {PaymentsApiPaymentsCheckoutCreateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + paymentsCheckoutCreate( + requestParameters: PaymentsApiPaymentsCheckoutCreateRequest, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .paymentsCheckoutCreate(requestParameters.system_slug, options) + .then((request) => request(axios, basePath)); + }, + /** + * Create a discount. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + paymentsDiscountsCreate( + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .paymentsDiscountsCreate(options) + .then((request) => request(axios, basePath)); + }, /** * Retrives the current user\'s completed orders. * @param {PaymentsApiPaymentsOrdersHistoryListRequest} requestParameters Request parameters. @@ -3176,6 +4907,13 @@ export const PaymentsApiFactory = function ( * @interface PaymentsApiPaymentsBasketsAddDiscountCreateRequest */ export interface PaymentsApiPaymentsBasketsAddDiscountCreateRequest { + /** + * + * @type {string} + * @memberof PaymentsApiPaymentsBasketsAddDiscountCreate + */ + readonly discount_code: string; + /** * * @type {string} @@ -3219,6 +4957,20 @@ export interface PaymentsApiPaymentsBasketsCreateFromProductCreateRequest { readonly system_slug: string; } +/** + * Request parameters for paymentsBasketsForSystemRetrieve operation in PaymentsApi. + * @export + * @interface PaymentsApiPaymentsBasketsForSystemRetrieveRequest + */ +export interface PaymentsApiPaymentsBasketsForSystemRetrieveRequest { + /** + * + * @type {string} + * @memberof PaymentsApiPaymentsBasketsForSystemRetrieve + */ + readonly system_slug: string; +} + /** * Request parameters for paymentsBasketsList operation in PaymentsApi. * @export @@ -3261,6 +5013,20 @@ export interface PaymentsApiPaymentsBasketsRetrieveRequest { readonly id: number; } +/** + * Request parameters for paymentsCheckoutCreate operation in PaymentsApi. + * @export + * @interface PaymentsApiPaymentsCheckoutCreateRequest + */ +export interface PaymentsApiPaymentsCheckoutCreateRequest { + /** + * + * @type {string} + * @memberof PaymentsApiPaymentsCheckoutCreate + */ + readonly system_slug: string; +} + /** * Request parameters for paymentsOrdersHistoryList operation in PaymentsApi. * @export @@ -3315,7 +5081,11 @@ export class PaymentsApi extends BaseAPI { options?: RawAxiosRequestConfig, ) { return PaymentsApiFp(this.configuration) - .paymentsBasketsAddDiscountCreate(requestParameters.system_slug, options) + .paymentsBasketsAddDiscountCreate( + requestParameters.discount_code, + requestParameters.system_slug, + options, + ) .then((request) => request(this.axios, this.basePath)); } @@ -3355,6 +5125,22 @@ export class PaymentsApi extends BaseAPI { .then((request) => request(this.axios, this.basePath)); } + /** + * Returns or creates a basket for the current user and system. + * @param {PaymentsApiPaymentsBasketsForSystemRetrieveRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PaymentsApi + */ + public paymentsBasketsForSystemRetrieve( + requestParameters: PaymentsApiPaymentsBasketsForSystemRetrieveRequest, + options?: RawAxiosRequestConfig, + ) { + return PaymentsApiFp(this.configuration) + .paymentsBasketsForSystemRetrieve(requestParameters.system_slug, options) + .then((request) => request(this.axios, this.basePath)); + } + /** * Retrives the current user\'s baskets, one per system. * @param {PaymentsApiPaymentsBasketsListRequest} requestParameters Request parameters. @@ -3392,6 +5178,34 @@ export class PaymentsApi extends BaseAPI { .then((request) => request(this.axios, this.basePath)); } + /** + * Generates and returns the form payload for the current basket for the specified system, which can be used to start the checkout process. + * @param {PaymentsApiPaymentsCheckoutCreateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PaymentsApi + */ + public paymentsCheckoutCreate( + requestParameters: PaymentsApiPaymentsCheckoutCreateRequest, + options?: RawAxiosRequestConfig, + ) { + return PaymentsApiFp(this.configuration) + .paymentsCheckoutCreate(requestParameters.system_slug, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Create a discount. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PaymentsApi + */ + public paymentsDiscountsCreate(options?: RawAxiosRequestConfig) { + return PaymentsApiFp(this.configuration) + .paymentsDiscountsCreate(options) + .then((request) => request(this.axios, this.basePath)); + } + /** * Retrives the current user\'s completed orders. * @param {PaymentsApiPaymentsOrdersHistoryListRequest} requestParameters Request parameters. diff --git a/src/services/ecommerce/meta/hooks.ts b/src/services/ecommerce/meta/hooks.ts new file mode 100644 index 0000000..ab68f30 --- /dev/null +++ b/src/services/ecommerce/meta/hooks.ts @@ -0,0 +1,24 @@ +import { useQuery } from "@tanstack/react-query"; +import { metaApi } from "../client"; + +const useMetaIntegratedSystemsList = () => + useQuery({ + queryKey: ["metaIntegratedSystems"], + queryFn: async () => { + const response = await metaApi.metaIntegratedSystemList(); + return response.data; + }, + }); + +const useMetaProductsList = (systemSlug: string) => + useQuery({ + queryKey: ["metaProducts"], + queryFn: async () => { + const response = await metaApi.metaProductList({ + system__slug: systemSlug, + }); + return response.data; + }, + }); + +export { useMetaIntegratedSystemsList, useMetaProductsList }; diff --git a/src/services/ecommerce/payments/hooks.ts b/src/services/ecommerce/payments/hooks.ts new file mode 100644 index 0000000..3961369 --- /dev/null +++ b/src/services/ecommerce/payments/hooks.ts @@ -0,0 +1,84 @@ +import { + useQuery, + useQueryClient, + UseQueryOptions, + useMutation, +} from "@tanstack/react-query"; +import { paymentsApi } from "../client"; +import type { + PaymentsApiPaymentsBasketsListRequest, + PaymentsApiPaymentsBasketsCreateFromProductCreateRequest, + PaymentsApiPaymentsBasketsAddDiscountCreateRequest, + PaymentsApiPaymentsCheckoutCreateRequest, +} from "../generated/v0/api"; + +type ExtraQueryOpts = Omit; + +const usePaymentsBasketList = ( + options: PaymentsApiPaymentsBasketsListRequest, + opts: ExtraQueryOpts = {}, +) => + useQuery({ + queryKey: ["paymentsBaskets", options], + queryFn: async () => { + const response = await paymentsApi.paymentsBasketsList(options); + return response.data; + }, + ...opts, + }); + +const usePaymentsBasketRetrieve = (id: number, opts: ExtraQueryOpts = {}) => { + return useQuery({ + queryKey: ["paymentsBaskets", id], + queryFn: async () => { + const response = await paymentsApi.paymentsBasketsRetrieve({ id }); + return response.data; + }, + ...opts, + }); +}; + +const usePaymentsBaksetCreateFromProduct = () => { + const client = useQueryClient(); + return useMutation({ + mutationFn: ( + slugAndSku: PaymentsApiPaymentsBasketsCreateFromProductCreateRequest, + ) => + paymentsApi + .paymentsBasketsCreateFromProductCreate(slugAndSku) + .then((response) => response.data), + onSuccess: (_data) => { + client.invalidateQueries({ queryKey: ["paymentsBaskets", _data] }); + }, + }); +}; + +const usePaymentsBasketAddDiscount = () => { + const client = useQueryClient(); + return useMutation({ + mutationFn: (request: PaymentsApiPaymentsBasketsAddDiscountCreateRequest) => + paymentsApi + .paymentsBasketsAddDiscountCreate(request) + .then((response) => response.data), + onSuccess: (_data) => { + client.invalidateQueries({ queryKey: ["paymentsBaskets", _data] }); + }, + }); +}; + +const usePaymentsCheckoutStartCheckout = () => { + return useMutation({ + mutationFn: (request: PaymentsApiPaymentsCheckoutCreateRequest) => + paymentsApi + .paymentsCheckoutCreate(request) + .then((response) => response.data), + }); +}; + +export { + usePaymentsBasketList, + usePaymentsBasketRetrieve, + usePaymentsBaksetCreateFromProduct, + usePaymentsBasketAddDiscount, + usePaymentsCheckoutStartCheckout, +}; diff --git a/src/utils/misc.ts b/src/utils/misc.ts new file mode 100644 index 0000000..81b9049 --- /dev/null +++ b/src/utils/misc.ts @@ -0,0 +1,3 @@ +const pxToRem = (px: number) => `${px / 16}rem`; + +export { pxToRem }; diff --git a/src/utils/system.test.tsx b/src/utils/system.test.tsx new file mode 100644 index 0000000..09179e5 --- /dev/null +++ b/src/utils/system.test.tsx @@ -0,0 +1,23 @@ +import { getCurrentSystem } from "./system"; + +describe("getCurrentSystem", () => { + test("Returns one of the expected systems", () => { + const urlParams = new URLSearchParams(); + urlParams.set("mitxonline", "true"); + expect(getCurrentSystem(urlParams)).toBe("mitxonline"); + + urlParams.set("mitxpro", ""); + expect(getCurrentSystem(urlParams)).toBe("mitxpro"); + + urlParams.set("system", "mitxpro"); + expect(getCurrentSystem(urlParams)).toBe("mitxpro"); + }); + + test("Returns one of the expected systems when the URL has multiple query params", () => { + const urlParams = new URLSearchParams(); + urlParams.set("cat", "meow"); + urlParams.set("dog", "woof"); + urlParams.set("mitxonline", ""); + expect(getCurrentSystem(urlParams)).toBe("mitxonline"); + }); +}); diff --git a/src/utils/system.tsx b/src/utils/system.tsx new file mode 100644 index 0000000..a49ea0b --- /dev/null +++ b/src/utils/system.tsx @@ -0,0 +1,21 @@ +"use client"; + +const getCurrentSystem = (urlParams: URLSearchParams) => { + let system: string = ""; + + if (urlParams.has("mitxonline")) { + system = "mitxonline"; + } + + if (urlParams.has("mitxpro")) { + system = "mitxpro"; + } + + if (urlParams.has("system")) { + system = encodeURIComponent(urlParams.get("system") as string); + } + + return system; +}; + +export { getCurrentSystem }; diff --git a/yarn.lock b/yarn.lock index dba5c49..1b67a97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1199,14 +1199,14 @@ __metadata: languageName: node linkType: hard -"@mitodl/smoot-design@npm:^1.0.1": - version: 1.0.1 - resolution: "@mitodl/smoot-design@npm:1.0.1" +"@mitodl/smoot-design@npm:^1.1.1": + version: 1.1.1 + resolution: "@mitodl/smoot-design@npm:1.1.1" dependencies: "@emotion/react": "npm:^11.11.1" "@emotion/styled": "npm:^11.11.0" - "@mui/base": "npm:5.0.0-beta.61" - "@mui/lab": "npm:6.0.0-beta.14" + "@mui/base": "npm:5.0.0-beta.64" + "@mui/lab": "npm:6.0.0-beta.18" "@mui/material": "npm:^6.1.6" "@mui/material-nextjs": "npm:^6.1.6" "@mui/system": "npm:^6.1.6" @@ -1219,18 +1219,18 @@ __metadata: peerDependencies: react: "*" react-dom: "*" - checksum: 10c0/9d6fd1a0b2a42c283e92074ba46c04ba973164228210bf8bab6fe86a7a69060b28129ab11b839d8ccfff8122a15c434fd14db91b1df6ddb93ca2f115860a2864 + checksum: 10c0/6bfb90429c35cedddf2855af64b298214f42d21f65ccbaa561910e7c4fb1146269e8418174101d216f9bfff31d4fc8e3c8bbf1b5bb92e129da9c952708566352 languageName: node linkType: hard -"@mui/base@npm:5.0.0-beta.61": - version: 5.0.0-beta.61 - resolution: "@mui/base@npm:5.0.0-beta.61" +"@mui/base@npm:5.0.0-beta.64": + version: 5.0.0-beta.64 + resolution: "@mui/base@npm:5.0.0-beta.64" dependencies: "@babel/runtime": "npm:^7.26.0" "@floating-ui/react-dom": "npm:^2.1.1" "@mui/types": "npm:^7.2.19" - "@mui/utils": "npm:^6.1.6" + "@mui/utils": "npm:^6.1.10" "@popperjs/core": "npm:^2.11.8" clsx: "npm:^2.1.1" prop-types: "npm:^15.8.1" @@ -1241,33 +1241,33 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/446b886c0cc3b46f08fd26e265522be7d55c9da1e5417b48a93551b764bf4b0b79234c6d735d4eba8d71f1f34879fed7117057da9f27e0ff742f39107b3f54a6 + checksum: 10c0/1818bbe746bcf61772177e9b06c5a4d7afb50c9ce2cc57c8fb57bb074ee618cf629458748fee00bbc2b683aac06975fc34181f280972bb860795960c9e82c060 languageName: node linkType: hard -"@mui/core-downloads-tracker@npm:^6.2.0": - version: 6.2.0 - resolution: "@mui/core-downloads-tracker@npm:6.2.0" - checksum: 10c0/5bdfb204859f004d631fcd2d97bcf0ec41ca17a7e4a453f89ecd8e8d460b3cd26c6eb93a6170bdcc510bb481e028fa35fdcf17e6673ea593dba615b95477fde6 +"@mui/core-downloads-tracker@npm:^6.2.1": + version: 6.2.1 + resolution: "@mui/core-downloads-tracker@npm:6.2.1" + checksum: 10c0/873c95a54fc8c5520a22feb1e98855742ce76b88e2cb909c02a9a99200cbb0e80971458626c435dc290634aaa3f066c501a7b87c03626dd135fee7bf52a12e4e languageName: node linkType: hard -"@mui/lab@npm:6.0.0-beta.14": - version: 6.0.0-beta.14 - resolution: "@mui/lab@npm:6.0.0-beta.14" +"@mui/lab@npm:6.0.0-beta.18": + version: 6.0.0-beta.18 + resolution: "@mui/lab@npm:6.0.0-beta.18" dependencies: "@babel/runtime": "npm:^7.26.0" - "@mui/base": "npm:5.0.0-beta.61" - "@mui/system": "npm:^6.1.6" + "@mui/base": "npm:5.0.0-beta.64" + "@mui/system": "npm:^6.1.10" "@mui/types": "npm:^7.2.19" - "@mui/utils": "npm:^6.1.6" + "@mui/utils": "npm:^6.1.10" clsx: "npm:^2.1.1" prop-types: "npm:^15.8.1" peerDependencies: "@emotion/react": ^11.5.0 "@emotion/styled": ^11.3.0 - "@mui/material": ^6.1.6 - "@mui/material-pigment-css": ^6.1.6 + "@mui/material": ^6.1.10 + "@mui/material-pigment-css": ^6.1.10 "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -1280,13 +1280,13 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10c0/dc46e8c984cc5caa261df2d48f943967049ad6118e7ba52b1aa60cfb15dc7b6fb11d2f0129616987b49865a54133286867d57a5d1aeba318ec558e523e6437fa + checksum: 10c0/4c4a097851b66c2d9f2703ee58f0eaf746e8c25b3e8d1c8da899bd5fb6cf38830b5ef4a8f81cec3ee3a0e7f88d58ea5394640c890b8765844b9d092c7a30d67d languageName: node linkType: hard "@mui/material-nextjs@npm:^6.1.6, @mui/material-nextjs@npm:^6.1.8": - version: 6.2.0 - resolution: "@mui/material-nextjs@npm:6.2.0" + version: 6.2.1 + resolution: "@mui/material-nextjs@npm:6.2.1" dependencies: "@babel/runtime": "npm:^7.26.0" peerDependencies: @@ -1303,21 +1303,21 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10c0/f0cc91e4b394e782f6e8cc07a5b73df3f52d218671cc49e670f5c58e88bcea58cc3cce006fa0f7de0d8454c0a3edca5d65b28c61d917867e8cccb7355899602b + checksum: 10c0/247b15d6013257198798e97ea0d892d8b7e10506cdd834f22bdf08f7440fb300e085bfcac3932d3522d7cdd16dba20be2418bc8e532b84ebadef1ea54171af39 languageName: node linkType: hard "@mui/material@npm:^6.1.6, @mui/material@npm:^6.1.8": - version: 6.2.0 - resolution: "@mui/material@npm:6.2.0" + version: 6.2.1 + resolution: "@mui/material@npm:6.2.1" dependencies: "@babel/runtime": "npm:^7.26.0" - "@mui/core-downloads-tracker": "npm:^6.2.0" - "@mui/system": "npm:^6.2.0" - "@mui/types": "npm:^7.2.19" - "@mui/utils": "npm:^6.2.0" + "@mui/core-downloads-tracker": "npm:^6.2.1" + "@mui/system": "npm:^6.2.1" + "@mui/types": "npm:^7.2.20" + "@mui/utils": "npm:^6.2.1" "@popperjs/core": "npm:^2.11.8" - "@types/react-transition-group": "npm:^4.4.11" + "@types/react-transition-group": "npm:^4.4.12" clsx: "npm:^2.1.1" csstype: "npm:^3.1.3" prop-types: "npm:^15.8.1" @@ -1326,7 +1326,7 @@ __metadata: peerDependencies: "@emotion/react": ^11.5.0 "@emotion/styled": ^11.3.0 - "@mui/material-pigment-css": ^6.2.0 + "@mui/material-pigment-css": ^6.2.1 "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -1339,16 +1339,16 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10c0/e01d719b3d9ffc7bec6ff277f272505c8caa6406a1c520b28b5abb389a78bb103893a815a78e9fe10f20b62fbc4c95c787ab31b8d50410465a683e421286b33c + checksum: 10c0/9ebef984b0aeec933290bf4d984bed02a984e30d0dbfeb473becbcf7c284bbe058d5dc1302445abe4a799750d415ea4893df18fa41545cf191f6fa5bc0d229a4 languageName: node linkType: hard -"@mui/private-theming@npm:^6.2.0": - version: 6.2.0 - resolution: "@mui/private-theming@npm:6.2.0" +"@mui/private-theming@npm:^6.2.1": + version: 6.2.1 + resolution: "@mui/private-theming@npm:6.2.1" dependencies: "@babel/runtime": "npm:^7.26.0" - "@mui/utils": "npm:^6.2.0" + "@mui/utils": "npm:^6.2.1" prop-types: "npm:^15.8.1" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -1356,13 +1356,13 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/49f563276747c8e2a133c2cf81713ebd5231cce8c24940ddc72d470477218621c0b5a203e0e4729d276d4d3eb1670fc15afe7b497719b84851cbc798b3a52bda + checksum: 10c0/15003060e88264e0247f0e0fc63ca696f3b326987d079615eee40670b77fd0c021ffcf8f650a33e84e0ac790075ab00bc8a9d577aa842298b889676dc6f06ef9 languageName: node linkType: hard -"@mui/styled-engine@npm:^6.2.0": - version: 6.2.0 - resolution: "@mui/styled-engine@npm:6.2.0" +"@mui/styled-engine@npm:^6.2.1": + version: 6.2.1 + resolution: "@mui/styled-engine@npm:6.2.1" dependencies: "@babel/runtime": "npm:^7.26.0" "@emotion/cache": "npm:^11.13.5" @@ -1379,19 +1379,19 @@ __metadata: optional: true "@emotion/styled": optional: true - checksum: 10c0/9831015df8057ce99db21e611410b4eaf947c959fbbf1cb4652c77288ae2db176cd10a1c7a8a2f1d7f80b0478075beaec3c0338a8c3ad146695f169f22582f5d + checksum: 10c0/3e95744b642b41afde7e5040fc428dbcf01a4a3937c859380b19a3ec3e23ebb460cb3681cf86773c13ea5be76f64ee071afa8ded9d38850bd5b90710623a8549 languageName: node linkType: hard -"@mui/system@npm:^6.1.6, @mui/system@npm:^6.2.0": - version: 6.2.0 - resolution: "@mui/system@npm:6.2.0" +"@mui/system@npm:^6.1.10, @mui/system@npm:^6.1.6, @mui/system@npm:^6.2.1": + version: 6.2.1 + resolution: "@mui/system@npm:6.2.1" dependencies: "@babel/runtime": "npm:^7.26.0" - "@mui/private-theming": "npm:^6.2.0" - "@mui/styled-engine": "npm:^6.2.0" - "@mui/types": "npm:^7.2.19" - "@mui/utils": "npm:^6.2.0" + "@mui/private-theming": "npm:^6.2.1" + "@mui/styled-engine": "npm:^6.2.1" + "@mui/types": "npm:^7.2.20" + "@mui/utils": "npm:^6.2.1" clsx: "npm:^2.1.1" csstype: "npm:^3.1.3" prop-types: "npm:^15.8.1" @@ -1407,28 +1407,28 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10c0/1b593985fe0428cc945e397e948500f91d9f5f8b1610cdd47b3e55dbfbe54a4467fe8af333e8261545553cfe71b394cf4762e7f93944d8bbafe0d4090310639e + checksum: 10c0/94aef42804fa4052b7ceabb41979b9192c432f0ae8bb74a450c604426eb9813c0066e646966704f30bbb262e75629bed05c9f87e0753e5416d6e903e6c870e23 languageName: node linkType: hard -"@mui/types@npm:^7.2.19": - version: 7.2.19 - resolution: "@mui/types@npm:7.2.19" +"@mui/types@npm:^7.2.19, @mui/types@npm:^7.2.20": + version: 7.2.20 + resolution: "@mui/types@npm:7.2.20" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/9c390d7eddc7e7c396852202fdca021aee275391bc7f48d0b6458748bf75eebb34c73109958692655ba5e72946cf47db2c0c7d2e1c26be568599ed65c931d080 + checksum: 10c0/257285386903fe0705ac6d53d0ece700323b7bc9be5239ab448b2c6523965b3cadbb636f2aec8ff60354180f53daf78df687d6828a75ad220b0f71fb5a117b5e languageName: node linkType: hard -"@mui/utils@npm:^6.1.6, @mui/utils@npm:^6.2.0": - version: 6.2.0 - resolution: "@mui/utils@npm:6.2.0" +"@mui/utils@npm:^6.1.10, @mui/utils@npm:^6.2.1": + version: 6.2.1 + resolution: "@mui/utils@npm:6.2.1" dependencies: "@babel/runtime": "npm:^7.26.0" - "@mui/types": "npm:^7.2.19" + "@mui/types": "npm:^7.2.20" "@types/prop-types": "npm:^15.7.14" clsx: "npm:^2.1.1" prop-types: "npm:^15.8.1" @@ -1439,7 +1439,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/a010b21f16673ece7f7c05e9c8a26a3db7d1a9e5557f2669178049bdd6f64525983eee9e6e3837221ff9261981048166716f4d37ae8cc77a2f9f525514b83496 + checksum: 10c0/1c81a5d3918fda0c8518e726a4630779f9b1145306e42a72f6f5264b9c30527fc31caea6a429457ad6eee17be40636240a0e57159c881abae0c266628f7a3615 languageName: node linkType: hard @@ -1678,11 +1678,11 @@ __metadata: linkType: hard "@remixicon/react@npm:^4.2.0": - version: 4.5.0 - resolution: "@remixicon/react@npm:4.5.0" + version: 4.6.0 + resolution: "@remixicon/react@npm:4.6.0" peerDependencies: react: ">=18.2.0" - checksum: 10c0/90b8821862dbd3e22a91b848b43e631aa62dc8ed24381d755d07a66f994e8af46ee90842aa70c8cc86e7fe7d20a148f7e4d6ed7e5c73c1eb35ca102244788fd1 + checksum: 10c0/52e21dabd5b5149206f2ef2d8ff59e38a0b7f4a043435ba65b1b8d1c41c755daea84da6c64e80f2ada0e582b4436366dbcef318add96ec11393b08b259b49df8 languageName: node linkType: hard @@ -2250,7 +2250,7 @@ __metadata: languageName: node linkType: hard -"@types/react-transition-group@npm:^4.4.11": +"@types/react-transition-group@npm:^4.4.12": version: 4.4.12 resolution: "@types/react-transition-group@npm:4.4.12" peerDependencies: @@ -2259,13 +2259,13 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^18, @types/react@npm:^18.0.26": - version: 18.3.16 - resolution: "@types/react@npm:18.3.16" +"@types/react@npm:^18": + version: 18.3.17 + resolution: "@types/react@npm:18.3.17" dependencies: "@types/prop-types": "npm:*" csstype: "npm:^3.0.2" - checksum: 10c0/9113d3003865eda07be0fb596f5bd8d7784b8900675090c56e75bdee45499f0b0de2481cbbeb5980c3b4ad18f234a49f39c9e62fd7b89a4e8530c74789f739bd + checksum: 10c0/475191e9cd0ab5bef35cc1868295d6526cd78b25c1bb816f0747a1e92261688305f81c0e29aff52e6ea70397f133e34c399dc936cb5072f1acf465d9daacc0da languageName: node linkType: hard @@ -2328,14 +2328,14 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/eslint-plugin@npm:^8.13.0": - version: 8.18.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.18.0" + version: 8.18.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.18.1" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.18.0" - "@typescript-eslint/type-utils": "npm:8.18.0" - "@typescript-eslint/utils": "npm:8.18.0" - "@typescript-eslint/visitor-keys": "npm:8.18.0" + "@typescript-eslint/scope-manager": "npm:8.18.1" + "@typescript-eslint/type-utils": "npm:8.18.1" + "@typescript-eslint/utils": "npm:8.18.1" + "@typescript-eslint/visitor-keys": "npm:8.18.1" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" @@ -2344,23 +2344,23 @@ __metadata: "@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10c0/c338da1b96c41d7b94401a6711659d0fef3acb691eff7a958f9d3aa0442a858830daad67e3575288a4f4669572e2b690517a513519b404a465ad68fe0a82d3ec + checksum: 10c0/7994d323228f3fc3ec124291cd02761251bcd9a5a6356001d2cb8f68abdb400c3cfbeb343d6941d8e6b6c8d2d616a278bbb3b6d9ed839ba5148a05f60a1f67b4 languageName: node linkType: hard "@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": - version: 8.18.0 - resolution: "@typescript-eslint/parser@npm:8.18.0" + version: 8.18.1 + resolution: "@typescript-eslint/parser@npm:8.18.1" dependencies: - "@typescript-eslint/scope-manager": "npm:8.18.0" - "@typescript-eslint/types": "npm:8.18.0" - "@typescript-eslint/typescript-estree": "npm:8.18.0" - "@typescript-eslint/visitor-keys": "npm:8.18.0" + "@typescript-eslint/scope-manager": "npm:8.18.1" + "@typescript-eslint/types": "npm:8.18.1" + "@typescript-eslint/typescript-estree": "npm:8.18.1" + "@typescript-eslint/visitor-keys": "npm:8.18.1" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10c0/d3a062511c24dfcf522a645db1153022d49aa3bb05e288c22474cf04dc1d836f877eb9d2733947e448981ffb16e4de50d4ebe7570a268733a641f228ca6c4849 + checksum: 10c0/23ab30b3f00b86108137e7df03710a088046ead3582595b0f8e17d5062770365e24e0a1ae3398bb3a1c29aa0f05a0de30887e2e0f6fb86163e878dd0eed1b25c languageName: node linkType: hard @@ -2402,28 +2402,28 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.18.0": - version: 8.18.0 - resolution: "@typescript-eslint/scope-manager@npm:8.18.0" +"@typescript-eslint/scope-manager@npm:8.18.1": + version: 8.18.1 + resolution: "@typescript-eslint/scope-manager@npm:8.18.1" dependencies: - "@typescript-eslint/types": "npm:8.18.0" - "@typescript-eslint/visitor-keys": "npm:8.18.0" - checksum: 10c0/6bf6532fd43f2b55b9b47fa8b0217c5b5a03f022e869a6a21228fc3ae04c0ac6c5ae5d6026866d189ba424d2f98cc6fbd2a34f909d241c9b86c031afd808f90c + "@typescript-eslint/types": "npm:8.18.1" + "@typescript-eslint/visitor-keys": "npm:8.18.1" + checksum: 10c0/97c503b2ece79b6c99ca8e6a5f1f40855cf72f17fbf05e42e62d19c2666e7e6f5df9bf71f13dbc4720c5ee0397670ba8052482a90441fbffa901da5f2e739565 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.18.0": - version: 8.18.0 - resolution: "@typescript-eslint/type-utils@npm:8.18.0" +"@typescript-eslint/type-utils@npm:8.18.1": + version: 8.18.1 + resolution: "@typescript-eslint/type-utils@npm:8.18.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.18.0" - "@typescript-eslint/utils": "npm:8.18.0" + "@typescript-eslint/typescript-estree": "npm:8.18.1" + "@typescript-eslint/utils": "npm:8.18.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.3.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10c0/c0fcf201c3b53f9374c0571198a639c81536170141caa08fd0f47094a596b1f82f839a849eac5832f954345c567dccb45b2ee1c0872c513331165f7bcb812396 + checksum: 10c0/cfe5362a22fa5e18a2662928904da024e42c84cb58a46238b9b61edafcd046f53c9505637176c8cd1c386165c6a6ed15a2b51700495cad6c20e0e33499d483a1 languageName: node linkType: hard @@ -2441,10 +2441,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.18.0": - version: 8.18.0 - resolution: "@typescript-eslint/types@npm:8.18.0" - checksum: 10c0/2dd7468c3f1c305545268b72c3a333488e6ab1b628c5f65081d895866422b9376c21634a7aac437805f84b22e352b6a8fc4dcf925ef4a8fd7d1898b8359f71be +"@typescript-eslint/types@npm:8.18.1": + version: 8.18.1 + resolution: "@typescript-eslint/types@npm:8.18.1" + checksum: 10c0/0a2ca5f7cdebcc844b6bc1e5afc5d83b563f55917d20e3fea3a17ed39c54b003178e26b5ec535113f45c93c569b46628d9a67defa70c01cbdfa801573fed69a2 languageName: node linkType: hard @@ -2485,12 +2485,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.18.0, @typescript-eslint/typescript-estree@npm:^8.13.0": - version: 8.18.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.18.0" +"@typescript-eslint/typescript-estree@npm:8.18.1, @typescript-eslint/typescript-estree@npm:^8.13.0": + version: 8.18.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.18.1" dependencies: - "@typescript-eslint/types": "npm:8.18.0" - "@typescript-eslint/visitor-keys": "npm:8.18.0" + "@typescript-eslint/types": "npm:8.18.1" + "@typescript-eslint/visitor-keys": "npm:8.18.1" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" @@ -2499,22 +2499,22 @@ __metadata: ts-api-utils: "npm:^1.3.0" peerDependencies: typescript: ">=4.8.4 <5.8.0" - checksum: 10c0/87b432b190b627314f007b17b2371898db78baaa3df67a0d9a94d080d88a7a307906b54a735084cacef37f6421e2b9c3320040617e73fe54eac2bf22c610f1ec + checksum: 10c0/7ecb061dc63c729b23f4f15db5736ca93b1ae633108400e6c31cf8af782494912f25c3683f9f952dbfd10cb96031caba247a1ad406abf5d163639a00ac3ce5a3 languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.18.0, @typescript-eslint/utils@npm:^6.0.0 || ^7.0.0 || ^8.0.0": - version: 8.18.0 - resolution: "@typescript-eslint/utils@npm:8.18.0" +"@typescript-eslint/utils@npm:8.18.1, @typescript-eslint/utils@npm:^6.0.0 || ^7.0.0 || ^8.0.0": + version: 8.18.1 + resolution: "@typescript-eslint/utils@npm:8.18.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:8.18.0" - "@typescript-eslint/types": "npm:8.18.0" - "@typescript-eslint/typescript-estree": "npm:8.18.0" + "@typescript-eslint/scope-manager": "npm:8.18.1" + "@typescript-eslint/types": "npm:8.18.1" + "@typescript-eslint/typescript-estree": "npm:8.18.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10c0/58a2fc1e404d1f905c2a958d995824eb4abc6e73836b186717550677f8b1d17954acc369feddb83277350915388bc3d8b721423c37777b8b8017fc29c89ec6ee + checksum: 10c0/1e29408bd8fbda9f3386dabdb2b7471dacff28342d5bd6521ca3b7932df0cae100030d2eac75d946a82cbefa33f78000eed4ce789128fdea069ffeabd4429d80 languageName: node linkType: hard @@ -2556,13 +2556,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.18.0": - version: 8.18.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.18.0" +"@typescript-eslint/visitor-keys@npm:8.18.1": + version: 8.18.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.18.1" dependencies: - "@typescript-eslint/types": "npm:8.18.0" + "@typescript-eslint/types": "npm:8.18.1" eslint-visitor-keys: "npm:^4.2.0" - checksum: 10c0/d4cdc2adab553098b5be7117fb7df76fb66cfd380528881a0a8c2a9eee03bf8baddda07d15ca0bd3ed8b35c379b3f449292183df18e3e81898dbcadafcb708b8 + checksum: 10c0/68651ae1825dbd660ea39b4e1d1618f6ad0026fa3a04aecec296750977cab316564e3e2ace8edbebf1ae86bd17d86acc98cac7b6e9aad4e1c666bd26f18706ad languageName: node linkType: hard @@ -2902,7 +2902,7 @@ __metadata: languageName: node linkType: hard -"arraybuffer.prototype.slice@npm:^1.0.3": +"arraybuffer.prototype.slice@npm:^1.0.4": version: 1.0.4 resolution: "arraybuffer.prototype.slice@npm:1.0.4" dependencies: @@ -3189,7 +3189,7 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": +"call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": version: 1.0.8 resolution: "call-bind@npm:1.0.8" dependencies: @@ -3240,9 +3240,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001688": - version: 1.0.30001688 - resolution: "caniuse-lite@npm:1.0.30001688" - checksum: 10c0/2ef3145ac69ea5faf403b613912a3a72006db2e004e58abcf40dc89904aa05568032b5a6dcfb267556944fd380a9b018ad645f93d84e543bed3471e4950a89f4 + version: 1.0.30001689 + resolution: "caniuse-lite@npm:1.0.30001689" + checksum: 10c0/51cf99751dddfba24e13556ae0e0f38c062f76d49f2e24cce3d28e71a0325ca6fe04fe51b4a0e8467d601d94e72fea84f160bf577e7cbb5677f14ac673b6da20 languageName: node linkType: hard @@ -3916,13 +3916,13 @@ __metadata: linkType: hard "dunder-proto@npm:^1.0.0": - version: 1.0.0 - resolution: "dunder-proto@npm:1.0.0" + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" dependencies: - call-bind-apply-helpers: "npm:^1.0.0" + call-bind-apply-helpers: "npm:^1.0.1" es-errors: "npm:^1.3.0" gopd: "npm:^1.2.0" - checksum: 10c0/b321e5cbf64f0a4c786b0b3dc187eb5197a83f6e05a1e11b86db25251b3ae6747c4b805d9e0a4fbf481d22a86a539dc75f82d883daeac7fc2ce4bd72ff5ef5a2 + checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031 languageName: node linkType: hard @@ -3934,9 +3934,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.73": - version: 1.5.73 - resolution: "electron-to-chromium@npm:1.5.73" - checksum: 10c0/b97118d469f2b3b7a816932004cd36d82879829904ca4a8daf70eaefbe686a23afa6e39e0ad0cdc39d00a9ebab97160d072b786fdeb6964f13fb15aa688958f1 + version: 1.5.74 + resolution: "electron-to-chromium@npm:1.5.74" + checksum: 10c0/1a93119adbdeb0bba4c29e3bad5a48e6a4626ae50fbff2bc5c207f32e67ed64a5d8db6500befb44080359be3b18be7bf830fb920d5199d935be95bb9f97deb10 languageName: node linkType: hard @@ -4018,56 +4018,58 @@ __metadata: linkType: hard "es-abstract@npm:^1.17.5, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3, es-abstract@npm:^1.23.5": - version: 1.23.5 - resolution: "es-abstract@npm:1.23.5" + version: 1.23.6 + resolution: "es-abstract@npm:1.23.6" dependencies: array-buffer-byte-length: "npm:^1.0.1" - arraybuffer.prototype.slice: "npm:^1.0.3" + arraybuffer.prototype.slice: "npm:^1.0.4" available-typed-arrays: "npm:^1.0.7" - call-bind: "npm:^1.0.7" + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" data-view-buffer: "npm:^1.0.1" data-view-byte-length: "npm:^1.0.1" data-view-byte-offset: "npm:^1.0.0" - es-define-property: "npm:^1.0.0" + es-define-property: "npm:^1.0.1" es-errors: "npm:^1.3.0" es-object-atoms: "npm:^1.0.0" es-set-tostringtag: "npm:^2.0.3" - es-to-primitive: "npm:^1.2.1" - function.prototype.name: "npm:^1.1.6" - get-intrinsic: "npm:^1.2.4" + es-to-primitive: "npm:^1.3.0" + function.prototype.name: "npm:^1.1.7" + get-intrinsic: "npm:^1.2.6" get-symbol-description: "npm:^1.0.2" globalthis: "npm:^1.0.4" - gopd: "npm:^1.0.1" + gopd: "npm:^1.2.0" has-property-descriptors: "npm:^1.0.2" - has-proto: "npm:^1.0.3" - has-symbols: "npm:^1.0.3" + has-proto: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" hasown: "npm:^2.0.2" - internal-slot: "npm:^1.0.7" + internal-slot: "npm:^1.1.0" is-array-buffer: "npm:^3.0.4" is-callable: "npm:^1.2.7" - is-data-view: "npm:^1.0.1" + is-data-view: "npm:^1.0.2" is-negative-zero: "npm:^2.0.3" - is-regex: "npm:^1.1.4" + is-regex: "npm:^1.2.1" is-shared-array-buffer: "npm:^1.0.3" - is-string: "npm:^1.0.7" + is-string: "npm:^1.1.1" is-typed-array: "npm:^1.1.13" - is-weakref: "npm:^1.0.2" + is-weakref: "npm:^1.1.0" + math-intrinsics: "npm:^1.0.0" object-inspect: "npm:^1.13.3" object-keys: "npm:^1.1.1" object.assign: "npm:^4.1.5" regexp.prototype.flags: "npm:^1.5.3" - safe-array-concat: "npm:^1.1.2" - safe-regex-test: "npm:^1.0.3" - string.prototype.trim: "npm:^1.2.9" - string.prototype.trimend: "npm:^1.0.8" + safe-array-concat: "npm:^1.1.3" + safe-regex-test: "npm:^1.1.0" + string.prototype.trim: "npm:^1.2.10" + string.prototype.trimend: "npm:^1.0.9" string.prototype.trimstart: "npm:^1.0.8" typed-array-buffer: "npm:^1.0.2" typed-array-byte-length: "npm:^1.0.1" - typed-array-byte-offset: "npm:^1.0.2" - typed-array-length: "npm:^1.0.6" + typed-array-byte-offset: "npm:^1.0.3" + typed-array-length: "npm:^1.0.7" unbox-primitive: "npm:^1.0.2" - which-typed-array: "npm:^1.1.15" - checksum: 10c0/1f6f91da9cf7ee2c81652d57d3046621d598654d1d1b05c1578bafe5c4c2d3d69513901679bdca2de589f620666ec21de337e4935cec108a4ed0871d5ef04a5d + which-typed-array: "npm:^1.1.16" + checksum: 10c0/87c9cd85264f42e993ee2f7157c5e49c2866651bd7ff89a0799cc5bcfb962b19814e1f58c9970101072bab2a68a4fb859f094c6e8f161ba8042569431f0c1ec4 languageName: node linkType: hard @@ -4137,7 +4139,7 @@ __metadata: languageName: node linkType: hard -"es-to-primitive@npm:^1.2.1": +"es-to-primitive@npm:^1.3.0": version: 1.3.0 resolution: "es-to-primitive@npm:1.3.0" dependencies: @@ -4921,7 +4923,7 @@ __metadata: languageName: node linkType: hard -"function.prototype.name@npm:^1.1.6": +"function.prototype.name@npm:^1.1.6, function.prototype.name@npm:^1.1.7": version: 1.1.7 resolution: "function.prototype.name@npm:1.1.7" dependencies: @@ -4955,7 +4957,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6": +"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6": version: 1.2.6 resolution: "get-intrinsic@npm:1.2.6" dependencies: @@ -5139,7 +5141,7 @@ __metadata: languageName: node linkType: hard -"has-proto@npm:^1.0.3": +"has-proto@npm:^1.0.3, has-proto@npm:^1.2.0": version: 1.2.0 resolution: "has-proto@npm:1.2.0" dependencies: @@ -5359,7 +5361,7 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.7": +"internal-slot@npm:^1.0.7, internal-slot@npm:^1.1.0": version: 1.1.0 resolution: "internal-slot@npm:1.1.0" dependencies: @@ -5415,12 +5417,13 @@ __metadata: linkType: hard "is-array-buffer@npm:^3.0.4": - version: 3.0.4 - resolution: "is-array-buffer@npm:3.0.4" + version: 3.0.5 + resolution: "is-array-buffer@npm:3.0.5" dependencies: - call-bind: "npm:^1.0.2" - get-intrinsic: "npm:^1.2.1" - checksum: 10c0/42a49d006cc6130bc5424eae113e948c146f31f9d24460fc0958f855d9d810e6fd2e4519bf19aab75179af9c298ea6092459d8cafdec523cd19e529b26eab860 + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + get-intrinsic: "npm:^1.2.6" + checksum: 10c0/c5c9f25606e86dbb12e756694afbbff64bc8b348d1bc989324c037e1068695131930199d6ad381952715dad3a9569333817f0b1a72ce5af7f883ce802e49c83d languageName: node linkType: hard @@ -5491,7 +5494,7 @@ __metadata: languageName: node linkType: hard -"is-data-view@npm:^1.0.1": +"is-data-view@npm:^1.0.1, is-data-view@npm:^1.0.2": version: 1.0.2 resolution: "is-data-view@npm:1.0.2" dependencies: @@ -5550,11 +5553,11 @@ __metadata: linkType: hard "is-finalizationregistry@npm:^1.1.0": - version: 1.1.0 - resolution: "is-finalizationregistry@npm:1.1.0" + version: 1.1.1 + resolution: "is-finalizationregistry@npm:1.1.1" dependencies: - call-bind: "npm:^1.0.7" - checksum: 10c0/1cd94236bfb6e060fe2b973c8726a2782727f7d495b3e8e1d51d3e619c5a3345413706f555956eb5b12af15eba0414118f64a1b19d793ec36b5e6767a13836ac + call-bound: "npm:^1.0.3" + checksum: 10c0/818dff679b64f19e228a8205a1e2d09989a98e98def3a817f889208cfcbf918d321b251aadf2c05918194803ebd2eb01b14fc9d0b2bea53d984f4137bfca5e97 languageName: node linkType: hard @@ -5663,7 +5666,7 @@ __metadata: languageName: node linkType: hard -"is-regex@npm:^1.1.4, is-regex@npm:^1.2.1": +"is-regex@npm:^1.2.1": version: 1.2.1 resolution: "is-regex@npm:1.2.1" dependencies: @@ -5735,7 +5738,7 @@ __metadata: languageName: node linkType: hard -"is-weakref@npm:^1.0.2": +"is-weakref@npm:^1.0.2, is-weakref@npm:^1.1.0": version: 1.1.0 resolution: "is-weakref@npm:1.1.0" dependencies: @@ -5745,12 +5748,12 @@ __metadata: linkType: hard "is-weakset@npm:^2.0.3": - version: 2.0.3 - resolution: "is-weakset@npm:2.0.3" + version: 2.0.4 + resolution: "is-weakset@npm:2.0.4" dependencies: - call-bind: "npm:^1.0.7" - get-intrinsic: "npm:^1.2.4" - checksum: 10c0/8ad6141b6a400e7ce7c7442a13928c676d07b1f315ab77d9912920bf5f4170622f43126f111615788f26c3b1871158a6797c862233124507db0bcc33a9537d1a + call-bound: "npm:^1.0.3" + get-intrinsic: "npm:^1.2.6" + checksum: 10c0/6491eba08acb8dc9532da23cb226b7d0192ede0b88f16199e592e4769db0a077119c1f5d2283d1e0d16d739115f70046e887e477eb0e66cd90e1bb29f28ba647 languageName: node linkType: hard @@ -6750,18 +6753,21 @@ __metadata: linkType: hard "material-ui-popup-state@npm:^5.1.0": - version: 5.3.1 - resolution: "material-ui-popup-state@npm:5.3.1" + version: 5.3.3 + resolution: "material-ui-popup-state@npm:5.3.3" dependencies: "@babel/runtime": "npm:^7.20.6" "@types/prop-types": "npm:^15.7.3" - "@types/react": "npm:^18.0.26" classnames: "npm:^2.2.6" prop-types: "npm:^15.7.2" peerDependencies: "@mui/material": ^5.0.0 || ^6.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10c0/9321f52f0bd7625e34a51fce86176bdaf3bd419f55b8780c9fe0ebaa9340dc7fa72893e5954d2bd17f1955d76ea6a503343771abaf7cff2d37c24b67c80e03ae + "@types/react": ^16.8.0 || ^17 || ^18 || ^19 + react: ^16.8.0 || ^17 || ^18 || ^19 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/8e2a3c48394d0465ba4707f63db5d0e8f0f5832b42b55fe8612bc6cb279771acb289b1f3e2057dcfb98c0a5722bcb03be6fe614d4d0ef579130c016b85d46cb2 languageName: node linkType: hard @@ -8546,7 +8552,7 @@ __metadata: languageName: node linkType: hard -"safe-array-concat@npm:^1.1.2": +"safe-array-concat@npm:^1.1.2, safe-array-concat@npm:^1.1.3": version: 1.1.3 resolution: "safe-array-concat@npm:1.1.3" dependencies: @@ -9097,7 +9103,7 @@ __metadata: languageName: node linkType: hard -"string.prototype.trim@npm:^1.2.9": +"string.prototype.trim@npm:^1.2.10": version: 1.2.10 resolution: "string.prototype.trim@npm:1.2.10" dependencies: @@ -9112,7 +9118,7 @@ __metadata: languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.8": +"string.prototype.trimend@npm:^1.0.8, string.prototype.trimend@npm:^1.0.9": version: 1.0.9 resolution: "string.prototype.trimend@npm:1.0.9" dependencies: @@ -9507,9 +9513,9 @@ __metadata: linkType: hard "type-fest@npm:^4.26.1": - version: 4.30.1 - resolution: "type-fest@npm:4.30.1" - checksum: 10c0/7ee8084aa764c9703f92e4c3546e0943909a1b508c93a02f1e528b877803b4af2cd68982d1d36578738a14026d5f5a3049d840aa11fca1d690d13a1612222da1 + version: 4.30.2 + resolution: "type-fest@npm:4.30.2" + checksum: 10c0/c28db60ff57223fb23180e66bd9652fb3197fb533e9360f9ee76e66c3ccb6849b292df5e8fa5897f215f6685357dd31c946511da56be549cb5de9d42ac9ea67d languageName: node linkType: hard @@ -9537,7 +9543,7 @@ __metadata: languageName: node linkType: hard -"typed-array-byte-offset@npm:^1.0.2": +"typed-array-byte-offset@npm:^1.0.3": version: 1.0.3 resolution: "typed-array-byte-offset@npm:1.0.3" dependencies: @@ -9552,7 +9558,7 @@ __metadata: languageName: node linkType: hard -"typed-array-length@npm:^1.0.6": +"typed-array-length@npm:^1.0.7": version: 1.0.7 resolution: "typed-array-length@npm:1.0.7" dependencies: @@ -9594,14 +9600,14 @@ __metadata: linkType: hard "unbox-primitive@npm:^1.0.2": - version: 1.0.2 - resolution: "unbox-primitive@npm:1.0.2" + version: 1.1.0 + resolution: "unbox-primitive@npm:1.1.0" dependencies: - call-bind: "npm:^1.0.2" + call-bound: "npm:^1.0.3" has-bigints: "npm:^1.0.2" - has-symbols: "npm:^1.0.3" - which-boxed-primitive: "npm:^1.0.2" - checksum: 10c0/81ca2e81134167cc8f75fa79fbcc8a94379d6c61de67090986a2273850989dd3bae8440c163121b77434b68263e34787a675cbdcb34bb2f764c6b9c843a11b66 + has-symbols: "npm:^1.1.0" + which-boxed-primitive: "npm:^1.1.1" + checksum: 10c0/7dbd35ab02b0e05fe07136c72cb9355091242455473ec15057c11430129bab38b7b3624019b8778d02a881c13de44d63cd02d122ee782fb519e1de7775b5b982 languageName: node linkType: hard @@ -9627,7 +9633,7 @@ __metadata: "@emotion/react": "npm:^11.11.1" "@emotion/styled": "npm:^11.11.0" "@faker-js/faker": "npm:^9.0.0" - "@mitodl/smoot-design": "npm:^1.0.1" + "@mitodl/smoot-design": "npm:^1.1.1" "@mui/material": "npm:^6.1.8" "@mui/material-nextjs": "npm:^6.1.8" "@remixicon/react": "npm:^4.2.0" @@ -10032,7 +10038,7 @@ __metadata: languageName: node linkType: hard -"which-boxed-primitive@npm:^1.0.2, which-boxed-primitive@npm:^1.1.0": +"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": version: 1.1.1 resolution: "which-boxed-primitive@npm:1.1.1" dependencies: @@ -10078,7 +10084,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.16": +"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.16": version: 1.1.16 resolution: "which-typed-array@npm:1.1.16" dependencies: