From 7c345dbddd8ea28b28fa12a88364cf5d3b95e35a Mon Sep 17 00:00:00 2001 From: Fasih Mehmood Date: Mon, 19 Aug 2024 12:14:07 +0500 Subject: [PATCH] feat: group subscriptions by editable/non-editable WD-14147 --- .../SubscriptionDetails.tsx | 90 +------ .../SubscriptionList/ListCard/ListCard.tsx | 103 ++----- .../SubscriptionList/ListGroup/ListGroup.tsx | 22 +- .../SubscriptionList/SubscriptionList.tsx | 251 ++++++++++++++++-- .../SubscriptionStatusChip.test.tsx | 156 +++++++++++ .../SubscriptionStatusChip.tsx | 73 +++++ .../SubscriptionStatusChip/index.ts | 1 + static/sass/_pattern_subscriptions.scss | 6 + 8 files changed, 514 insertions(+), 188 deletions(-) create mode 100644 static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/SubscriptionStatusChip.test.tsx create mode 100644 static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/SubscriptionStatusChip.tsx create mode 100644 static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/index.ts diff --git a/static/js/src/advantage/react/components/Subscriptions/SubscriptionDetails/SubscriptionDetails.tsx b/static/js/src/advantage/react/components/Subscriptions/SubscriptionDetails/SubscriptionDetails.tsx index b1b008cf70c..4e13daa0b00 100644 --- a/static/js/src/advantage/react/components/Subscriptions/SubscriptionDetails/SubscriptionDetails.tsx +++ b/static/js/src/advantage/react/components/Subscriptions/SubscriptionDetails/SubscriptionDetails.tsx @@ -26,6 +26,7 @@ import RenewalButton from "../RenewalButton"; import SubscriptionCancel from "../SubscriptionCancel"; import SubscriptionEdit from "../SubscriptionEdit"; import DetailsContent from "./DetailsContent"; +import SubscriptionStatusChip from "../SubscriptionStatusChip"; type Props = { modalActive?: boolean; @@ -130,82 +131,19 @@ export const SubscriptionDetails = forwardRef( > Close - {subscription.statuses.is_expired ? ( - - ) : ( - <> - {subscription.type == "legacy" && subscription.renewal_id ? ( - <> - {subscription.statuses.is_renewed ? ( - - ) : ( - <> - {subscription.statuses.is_renewal_actionable && - subscription.statuses.is_renewable ? ( - - ) : null} - - )} - - ) : null} - {subscription.type == "monthly" || - subscription.type == "yearly" ? ( - <> - {subscription.statuses.is_subscription_active && - !subscription.statuses.is_cancelled ? ( - <> - {subscription.statuses.is_renewed ? ( - - ) : null} - {!subscription.statuses.is_renewed ? ( - - ) : null} - - ) : null} - {subscription.statuses.is_cancelled ? ( - - ) : null} - - ) : null} - {subscription.type == "trial" ? ( - <> - {subscription.statuses.is_subscription_active ? ( - <> - {subscription.statuses.is_renewed && - !subscription.statuses.is_cancelled ? ( - - ) : null} - - ) : null} - {subscription.statuses.is_cancelled ? ( - - ) : null} - - ) : null} - - )} + { + + {(data) => + data ? ( + + ) : ( + <> + ) + } + + } ); } + return ( - {subscription.statuses.is_expired ? ( - - ) : ( - <> - {subscription.type == "legacy" && subscription.renewal_id ? ( - <> - {subscription.statuses.is_renewed ? ( - - ) : ( - <> - {subscription.statuses.is_renewal_actionable && - subscription.statuses.is_renewable ? ( - - ) : null} - - )} - - ) : null} - {subscription.type == "monthly" || - subscription.type == "yearly" ? ( - <> - {subscription.statuses.is_subscription_active && - !subscription.statuses.is_cancelled ? ( - <> - {subscription.statuses.is_renewed ? ( - - ) : null} - {!subscription.statuses.is_renewed ? ( - - ) : null} - - ) : null} - {subscription.statuses.is_cancelled ? ( - - ) : null} - - ) : null} - {subscription.type == "trial" ? ( - <> - {subscription.statuses.is_subscription_active ? ( - <> - {subscription.statuses.is_renewed && - !subscription.statuses.is_cancelled ? ( - - ) : null} - - ) : null} - {subscription.statuses.is_cancelled ? ( - - ) : null} - - ) : null} - - )} + { + + {(data) => + data ? ( + + ) : ( + <> + ) + } + + } diff --git a/static/js/src/advantage/react/components/Subscriptions/SubscriptionList/ListGroup/ListGroup.tsx b/static/js/src/advantage/react/components/Subscriptions/SubscriptionList/ListGroup/ListGroup.tsx index 2d482552d2c..0b4f8e7bb64 100644 --- a/static/js/src/advantage/react/components/Subscriptions/SubscriptionList/ListGroup/ListGroup.tsx +++ b/static/js/src/advantage/react/components/Subscriptions/SubscriptionList/ListGroup/ListGroup.tsx @@ -5,14 +5,23 @@ import { useUserSubscriptions } from "advantage/react/hooks"; import RenewalSettings from "../RenewalSettings"; import { UserSubscriptionMarketplace } from "advantage/api/enum"; +import classNames from "classnames"; type Props = { children: ReactNode; title: string; + subtitle?: string; marketplace: UserSubscriptionMarketplace; + selfServiceable?: boolean; }; -const ListGroup = ({ children, title, marketplace }: Props): JSX.Element => { +const ListGroup = ({ + children, + title, + subtitle, + marketplace, + selfServiceable = true, +}: Props): JSX.Element => { const { data: renewableSubscriptions } = useUserSubscriptions({ select: selectAutoRenewableSubscriptionsByMarketplace(marketplace), }); @@ -22,7 +31,9 @@ const ListGroup = ({ children, title, marketplace }: Props): JSX.Element => { return (
{ positionNode.current = element; // Fire state change so that the menu rerenders now that the ref @@ -33,13 +44,18 @@ const ListGroup = ({ children, title, marketplace }: Props): JSX.Element => { {title} - {(renewableSubscriptions?.length ?? 0 > 0) ? ( + {(renewableSubscriptions?.length ?? 0 > 0) && selfServiceable ? ( ) : null}
+ {subtitle ? ( +
+ {subtitle} +
+ ) : null} {children}
); diff --git a/static/js/src/advantage/react/components/Subscriptions/SubscriptionList/SubscriptionList.tsx b/static/js/src/advantage/react/components/Subscriptions/SubscriptionList/SubscriptionList.tsx index 54dc8983b53..782bab11d58 100644 --- a/static/js/src/advantage/react/components/Subscriptions/SubscriptionList/SubscriptionList.tsx +++ b/static/js/src/advantage/react/components/Subscriptions/SubscriptionList/SubscriptionList.tsx @@ -1,5 +1,10 @@ +import type { ReactNode } from "react"; import { Spinner } from "@canonical/react-components"; -import { UserSubscriptionMarketplace } from "advantage/api/enum"; +import { + UserSubscriptionMachineType, + UserSubscriptionMarketplace, + UserSubscriptionType, +} from "advantage/api/enum"; import { useUserSubscriptions } from "advantage/react/hooks"; import { selectFreeSubscription, @@ -12,7 +17,174 @@ import { SelectedId } from "../Content/types"; import ListCard from "./ListCard"; import ListGroup from "./ListGroup"; -import { UserSubscription } from "advantage/api/types"; +import type { UserSubscription } from "advantage/api/types"; + +const dummySubs: UserSubscription[] = [ + { + account_id: "aAAue9JbQiMtnnmtzsFPAPD88aXCIRS-ZX7za_PeY-h8", + contract_id: "cAMYwgMQ1KA1WIw98RzhzTblyzzuhef4PvXmlRlAyorE", + currency: "USD", + current_number_of_machines: 13, + end_date: new Date("2025-08-13T05:45:39Z"), + entitlements: [], + id: "yearly||aAAue9JbQiMtnnmtzsFPAPD88aXCIRS-ZX7za_PeY-h8||cAMYwgMQ1KA1WIw98RzhzTblyzzuhef4PvXmlRlAyorE||sAGgme0Lwk3zjQ97yzy0DFdMVK3jZlHBpOOEyd_5TnQk", + listing_id: "lADMts0AWOPPhtiDf5wM6GtKItqyQ5ZK5UT5uCGprbqE", + machine_type: UserSubscriptionMachineType.Desktop, + marketplace: UserSubscriptionMarketplace.CanonicalUA, + max_tracking_reached: false, + number_of_active_machines: 0, + number_of_machines: 15, + period: null, + price: 450000, + product_name: "Ubuntu Pro Desktop + Support (24/7)", + renewal_id: null, + start_date: new Date("2024-08-13T08:25:10Z"), + statuses: { + has_access_to_support: true, + has_access_to_token: true, + has_pending_purchases: false, + is_cancellable: true, + is_cancelled: false, + is_downsizeable: false, + is_expired: false, + is_expiring: true, + is_in_grace_period: false, + is_renewable: false, + is_renewal_actionable: false, + is_renewed: true, + is_subscription_active: true, + is_subscription_auto_renewing: true, + is_trialled: false, + is_upsizeable: false, + should_present_auto_renewal: false, + }, + subscription_id: "sAGgme0Lwk3zjQ97yzy0DFdMVK3jZlHBpOOEyd_5TnQk", + type: UserSubscriptionType.Legacy, + }, + { + account_id: "aAAue9JbQiMtnnmtzsFPAPD88aXCIRS-ZX7za_PeY-h8", + contract_id: "cAMYwgMQ1KA1WIw98RzhzTblyzzuhef4PvXmlRlAyorE", + currency: "USD", + current_number_of_machines: 13, + end_date: new Date("2025-08-13T05:45:39Z"), + entitlements: [], + id: "yearly||aAAue9JbQiMtnnmtzsFPAPD88aXCIRS-ZX7za_PeY-h8||cAMYwgMQ1KA1WIw98RzhzTblyzzuhef4PvXmlRlAyorE||sAGgme0Lwk3zjQ97yzy0DFdMVK3jZlHBpOOEyd_5TnQk", + listing_id: "lADMts0AWOPPhtiDf5wM6GtKItqyQ5ZK5UT5uCGprbqE", + machine_type: UserSubscriptionMachineType.Desktop, + marketplace: UserSubscriptionMarketplace.CanonicalUA, + max_tracking_reached: false, + number_of_active_machines: 0, + number_of_machines: 15, + period: null, + price: 450000, + product_name: "Ubuntu Pro Desktop + Support (24/7)", + renewal_id: null, + start_date: new Date("2024-08-13T08:25:10Z"), + statuses: { + has_access_to_support: true, + has_access_to_token: true, + has_pending_purchases: false, + is_cancellable: true, + is_cancelled: false, + is_downsizeable: false, + is_expired: false, + is_expiring: false, + is_in_grace_period: false, + is_renewable: false, + is_renewal_actionable: false, + is_renewed: true, + is_subscription_active: true, + is_subscription_auto_renewing: true, + is_trialled: false, + is_upsizeable: false, + should_present_auto_renewal: false, + }, + subscription_id: "sAGgme0Lwk3zjQ97yzy0DFdMVK3jZlHBpOOEyd_5TnQk", + type: UserSubscriptionType.Legacy, + }, + { + account_id: "aAAue9JbQiMtnnmtzsFPAPD88aXCIRS-ZX7za_PeY-h8", + contract_id: "cAMYwgMQ1KA1WIw98RzhzTblyzzuhef4PvXmlRlAyorE", + currency: "USD", + current_number_of_machines: 13, + end_date: new Date("2025-08-13T05:45:39Z"), + entitlements: [], + id: "yearly||aAAue9JbQiMtnnmtzsFPAPD88aXCIRS-ZX7za_PeY-h8||cAMYwgMQ1KA1WIw98RzhzTblyzzuhef4PvXmlRlAyorE||sAGgme0Lwk3zjQ97yzy0DFdMVK3jZlHBpOOEyd_5TnQk", + listing_id: "lADMts0AWOPPhtiDf5wM6GtKItqyQ5ZK5UT5uCGprbqE", + machine_type: UserSubscriptionMachineType.Desktop, + marketplace: UserSubscriptionMarketplace.CanonicalUA, + max_tracking_reached: false, + number_of_active_machines: 0, + number_of_machines: 15, + period: null, + price: 450000, + product_name: "Ubuntu Pro Desktop + Support (24/7)", + renewal_id: null, + start_date: new Date("2024-08-13T08:25:10Z"), + statuses: { + has_access_to_support: true, + has_access_to_token: true, + has_pending_purchases: false, + is_cancellable: true, + is_cancelled: false, + is_downsizeable: false, + is_expired: false, + is_expiring: false, + is_in_grace_period: true, + is_renewable: false, + is_renewal_actionable: false, + is_renewed: true, + is_subscription_active: true, + is_subscription_auto_renewing: true, + is_trialled: false, + is_upsizeable: false, + should_present_auto_renewal: false, + }, + subscription_id: "sAGgme0Lwk3zjQ97yzy0DFdMVK3jZlHBpOOEyd_5TnQk", + type: UserSubscriptionType.Legacy, + }, + { + account_id: "aAAue9JbQiMtnnmtzsFPAPD88aXCIRS-ZX7za_PeY-h8", + contract_id: "cAMYwgMQ1KA1WIw98RzhzTblyzzuhef4PvXmlRlAyorE", + currency: "USD", + current_number_of_machines: 13, + end_date: new Date("2025-08-13T05:45:39Z"), + entitlements: [], + id: "yearly||aAAue9JbQiMtnnmtzsFPAPD88aXCIRS-ZX7za_PeY-h8||cAMYwgMQ1KA1WIw98RzhzTblyzzuhef4PvXmlRlAyorE||sAGgme0Lwk3zjQ97yzy0DFdMVK3jZlHBpOOEyd_5TnQk", + listing_id: "lADMts0AWOPPhtiDf5wM6GtKItqyQ5ZK5UT5uCGprbqE", + machine_type: UserSubscriptionMachineType.Desktop, + marketplace: UserSubscriptionMarketplace.CanonicalUA, + max_tracking_reached: false, + number_of_active_machines: 0, + number_of_machines: 15, + period: null, + price: 450000, + product_name: "Ubuntu Pro Desktop + Support (24/7)", + renewal_id: null, + start_date: new Date("2024-08-13T08:25:10Z"), + statuses: { + has_access_to_support: true, + has_access_to_token: true, + has_pending_purchases: false, + is_cancellable: true, + is_cancelled: false, + is_downsizeable: false, + is_expired: true, + is_expiring: false, + is_in_grace_period: false, + is_renewable: false, + is_renewal_actionable: false, + is_renewed: true, + is_subscription_active: true, + is_subscription_auto_renewing: true, + is_trialled: false, + is_upsizeable: false, + should_present_auto_renewal: false, + }, + subscription_id: "sAGgme0Lwk3zjQ97yzy0DFdMVK3jZlHBpOOEyd_5TnQk", + type: UserSubscriptionType.Legacy, + }, +]; type Props = { selectedId?: SelectedId; @@ -37,24 +209,45 @@ const SubscriptionList = ({ selectedId, onSetActive }: Props) => { return ; } // Sort the subscriptions so that the most recently started subscription is first. - const sortedUASubscriptions = - sortSubscriptionsByStartDate(uaSubscriptionsData); - const uaSubscriptions = sortedUASubscriptions.map((subscription) => ( - { - sendAnalyticsEvent({ - eventCategory: "Advantage", - eventAction: "subscription-change", - eventLabel: "ua subscription clicked", - }); - onSetActive(subscription.id); - }} - subscription={subscription} - /> - )); + const sortedUASubscriptions = sortSubscriptionsByStartDate( + uaSubscriptionsData.concat(dummySubs), + ); + + const groupedUASubscriptions = sortedUASubscriptions.reduce( + (prev, current) => { + const sub = ( + { + sendAnalyticsEvent({ + eventCategory: "Advantage", + eventAction: "subscription-change", + eventLabel: "ua subscription clicked", + }); + onSetActive(current.id); + }} + subscription={current} + /> + ); + + if ( + current.type === UserSubscriptionType.KeyActivated || + current.type === UserSubscriptionType.Legacy + ) { + prev.nonEditable.push(sub); + } else { + prev.editable.push(sub); + } + + return prev; + }, + { + editable: [] as ReactNode[], + nonEditable: [] as ReactNode[], + }, + ); const sortedBlenderSubscriptions = sortSubscriptionsByStartDate( blenderSubscriptionsData, @@ -95,13 +288,25 @@ const SubscriptionList = ({ selectedId, onSetActive }: Props) => { return (
- {sortedUASubscriptions.length ? ( + {groupedUASubscriptions.editable.length ? ( + + {groupedUASubscriptions.editable} + + ) : null} + + {groupedUASubscriptions.nonEditable.length ? ( - {uaSubscriptions} + {groupedUASubscriptions.nonEditable} ) : null} diff --git a/static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/SubscriptionStatusChip.test.tsx b/static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/SubscriptionStatusChip.test.tsx new file mode 100644 index 00000000000..43d2f5301ac --- /dev/null +++ b/static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/SubscriptionStatusChip.test.tsx @@ -0,0 +1,156 @@ +import { render, screen } from "@testing-library/react"; +import { UserSubscriptionType } from "advantage/api/enum"; +import { UserSubscription } from "advantage/api/types"; +import { + userSubscriptionFactory, + userSubscriptionStatusesFactory, +} from "advantage/tests/factories/api"; +import SubscriptionStatusChip from "./SubscriptionStatusChip"; + +const renderComponent = (subscription: UserSubscription) => { + return render( + + {(data) => ( +
+ {data?.status || "No status"} +
+ )} +
, + ); +}; + +describe("SubscriptionStatusChip Component", () => { + it("Should show 'Expired' chip when status is expired", () => { + renderComponent( + userSubscriptionFactory.build({ + statuses: userSubscriptionStatusesFactory.build({ + is_expired: true, + }), + }), + ); + + expect(screen.getByTestId("chip")).toBeInTheDocument(); + expect(screen.getByTestId("chip")).toHaveTextContent("Expired"); + expect(screen.getByTestId("chip")).toHaveClass("p-chip--negative"); + }); + + it("Should show 'Grace period' chip when is_in_grace_period is true", () => { + renderComponent( + userSubscriptionFactory.build({ + statuses: userSubscriptionStatusesFactory.build({ + is_in_grace_period: true, + }), + }), + ); + expect(screen.getByTestId("chip")).toHaveTextContent("Grace period"); + expect(screen.getByTestId("chip")).toHaveClass("p-chip--negative"); + }); + + it("Should show 'Expiring soon' chip when is_expiring is true", () => { + renderComponent( + userSubscriptionFactory.build({ + statuses: userSubscriptionStatusesFactory.build({ is_expiring: true }), + }), + ); + expect(screen.getByTestId("chip")).toHaveTextContent("Expiring soon"); + expect(screen.getByTestId("chip")).toHaveClass("p-chip--caution"); + }); + + it("Should show 'Renewed' chip for legacy subscriptions", () => { + renderComponent( + userSubscriptionFactory.build({ + statuses: { + is_renewed: true, + }, + type: UserSubscriptionType.Legacy, + renewal_id: "some-id", + }), + ); + expect(screen.getByTestId("chip")).toHaveTextContent("Renewed"); + expect(screen.getByTestId("chip")).toHaveClass("p-chip--positive"); + }); + + it("Should show 'Not renewed' chip for legacy subscriptions", () => { + renderComponent( + userSubscriptionFactory.build({ + statuses: { + is_renewed: false, + is_renewal_actionable: true, + is_renewable: true, + }, + type: UserSubscriptionType.Legacy, + renewal_id: "some-id", + }), + ); + expect(screen.getByTestId("chip")).toHaveTextContent("Not renewed"); + expect(screen.getByTestId("chip")).toHaveClass("p-chip--caution"); + }); + + it("Should show Auto-renewal on chip for active monthly/yearly/trial subscriptions", () => { + renderComponent( + userSubscriptionFactory.build({ + statuses: { + is_subscription_active: true, + is_cancelled: false, + is_renewed: true, + }, + type: UserSubscriptionType.Monthly, + }), + ); + expect(screen.getByTestId("chip")).toHaveTextContent("Auto-renewal on"); + expect(screen.getByTestId("chip")).toHaveClass("p-chip--positive"); + }); + + it("Should show Auto-renewal off chip for non-renewed monthly/yearly/trial subscriptions", () => { + renderComponent( + userSubscriptionFactory.build({ + statuses: { + is_subscription_active: true, + is_cancelled: false, + is_renewed: false, + }, + type: UserSubscriptionType.Monthly, + }), + ); + expect(screen.getByTestId("chip")).toHaveTextContent("Auto-renewal off"); + expect(screen.getByTestId("chip")).toHaveClass("p-chip--caution"); + }); + + it("Should show 'Cancelled' chip for cancelled monthly/yearly/trial subscriptions", () => { + renderComponent( + userSubscriptionFactory.build({ + statuses: { is_subscription_active: false, is_cancelled: true }, + type: UserSubscriptionType.Monthly, + }), + ); + expect(screen.getByTestId("chip")).toHaveTextContent("Cancelled"); + expect(screen.getByTestId("chip")).toHaveClass("p-chip--negative"); + }); + + it("Should show 'Active' chip for non-monthly/yearly/trial active subscriptions", () => { + renderComponent( + userSubscriptionFactory.build({ + statuses: { is_subscription_active: true, is_cancelled: false }, + type: UserSubscriptionType.KeyActivated, + }), + ); + expect(screen.getByTestId("chip")).toHaveTextContent("Active"); + expect(screen.getByTestId("chip")).toHaveClass("p-chip--positive"); + }); + + it("Should show nothing when no conditions are met", () => { + renderComponent( + userSubscriptionFactory.build({ + statuses: { + is_expired: false, + is_in_grace_period: false, + is_subscription_active: false, + is_cancelled: false, + is_renewed: false, + is_expiring: false, + }, + }), + ); + expect(screen.getByTestId("chip")).toHaveTextContent("No status"); + }); +}); diff --git a/static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/SubscriptionStatusChip.tsx b/static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/SubscriptionStatusChip.tsx new file mode 100644 index 00000000000..91d6730b846 --- /dev/null +++ b/static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/SubscriptionStatusChip.tsx @@ -0,0 +1,73 @@ +import type { ReactNode } from "react"; +import type { UserSubscription } from "advantage/api/types"; +import { UserSubscriptionType } from "advantage/api/enum"; + +type StatusType = "positive" | "negative" | "caution"; + +type Props = { + subscription: UserSubscription; + children: (data?: { status: string; type: StatusType }) => ReactNode; +}; + +export default function SubscriptionStatusChip({ + subscription, + children, +}: Props) { + const { + statuses: { + is_expired, + is_expiring, + is_in_grace_period, + is_renewed, + is_renewal_actionable, + is_renewable, + is_subscription_active, + is_cancelled, + }, + type, + renewal_id, + } = subscription; + + const getChip = (status: string, type: StatusType) => + children({ type, status }); + + if (is_expired || is_in_grace_period) { + return getChip(is_expired ? "Expired" : "Grace period", "negative"); + } + + if (is_expiring) { + return getChip("Expiring soon", "caution"); + } + + if (type === UserSubscriptionType.Legacy && renewal_id) { + if (is_renewed) { + return getChip("Renewed", "positive"); + } + if (is_renewal_actionable && is_renewable) { + return getChip("Not renewed", "caution"); + } + } + + if ( + [ + UserSubscriptionType.Monthly, + UserSubscriptionType.Yearly, + UserSubscriptionType.Trial, + ].includes(type) + ) { + if (is_subscription_active && !is_cancelled) { + return getChip( + is_renewed ? "Auto-renewal on" : "Auto-renewal off", + is_renewed ? "positive" : "caution", + ); + } + + if (is_cancelled) { + return getChip("Cancelled", "negative"); + } + } else if (is_subscription_active) { + return getChip("Active", "positive"); + } + + return children(); +} diff --git a/static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/index.ts b/static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/index.ts new file mode 100644 index 00000000000..06f7181da4c --- /dev/null +++ b/static/js/src/advantage/react/components/Subscriptions/SubscriptionStatusChip/index.ts @@ -0,0 +1 @@ +export { default } from "./SubscriptionStatusChip"; diff --git a/static/sass/_pattern_subscriptions.scss b/static/sass/_pattern_subscriptions.scss index c9f436586bb..fb51e890c06 100644 --- a/static/sass/_pattern_subscriptions.scss +++ b/static/sass/_pattern_subscriptions.scss @@ -111,6 +111,7 @@ // Move the actions down to the bottom of the container. align-self: end; border-top: 1px solid $colors--light-theme--border-default; + -moz-column-gap: $sp-medium; column-gap: $sp-medium; display: grid; // Move the actions down to the footer area. @@ -224,6 +225,11 @@ margin-bottom: $spv--large; } + .p-subscriptions__list-group-subtitle { + // Provide space before the first card. + margin-bottom: $spv--large; + } + .p-subscriptions__list-card-title { // Put the title and period on the same line. display: flex;