diff --git a/apps/portal/next-env.d.ts b/apps/portal/next-env.d.ts index 9edff1c7..c4b7818f 100644 --- a/apps/portal/next-env.d.ts +++ b/apps/portal/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/portal/src/app/globals.css b/apps/portal/src/app/globals.css index 5c5b0020..8aeafa59 100644 --- a/apps/portal/src/app/globals.css +++ b/apps/portal/src/app/globals.css @@ -228,7 +228,6 @@ @theme { /* Font Families */ --font-family-sans: var(--font-sans); - --font-family-heading: var(--font-display); --font-family-mono: var(--font-mono); /* Colors */ diff --git a/apps/portal/src/components/atoms/logo.tsx b/apps/portal/src/components/atoms/logo.tsx index 44110347..5d637735 100644 --- a/apps/portal/src/components/atoms/logo.tsx +++ b/apps/portal/src/components/atoms/logo.tsx @@ -25,12 +25,18 @@ export function Logo({ className = "", size = 32 }: LogoProps) { aria-label="Assist Solutions Logo" > {/* Top section - Light blue curved arrows */} - - + + {/* Bottom section - Dark blue curved arrows */} - - + + ); diff --git a/apps/portal/src/components/molecules/SectionCard/SectionCard.tsx b/apps/portal/src/components/molecules/SectionCard/SectionCard.tsx new file mode 100644 index 00000000..00e70f8f --- /dev/null +++ b/apps/portal/src/components/molecules/SectionCard/SectionCard.tsx @@ -0,0 +1,63 @@ +import type { ReactNode } from "react"; +import { cn } from "@/shared/utils"; + +type SectionTone = "primary" | "success" | "info" | "warning" | "danger" | "neutral"; + +const toneStyles: Record = { + primary: "bg-primary/10 text-primary", + success: "bg-success/10 text-success", + info: "bg-info/10 text-info", + warning: "bg-warning/10 text-warning", + danger: "bg-danger/10 text-danger", + neutral: "bg-neutral/10 text-neutral", +}; + +interface SectionCardProps { + icon: ReactNode; + title: string; + subtitle?: string | undefined; + tone?: SectionTone; + actions?: ReactNode | undefined; + children: ReactNode; + className?: string | undefined; +} + +export function SectionCard({ + icon, + title, + subtitle, + tone = "primary", + actions, + children, + className, +}: SectionCardProps) { + return ( +
+
+
+
+
+ {icon} +
+
+

{title}

+ {subtitle &&

{subtitle}

} +
+
+ {actions &&
{actions}
} +
+
+
{children}
+
+ ); +} diff --git a/apps/portal/src/components/molecules/SectionCard/index.ts b/apps/portal/src/components/molecules/SectionCard/index.ts new file mode 100644 index 00000000..1bee550b --- /dev/null +++ b/apps/portal/src/components/molecules/SectionCard/index.ts @@ -0,0 +1 @@ +export { SectionCard } from "./SectionCard"; diff --git a/apps/portal/src/components/molecules/index.ts b/apps/portal/src/components/molecules/index.ts index 21241450..86b11e91 100644 --- a/apps/portal/src/components/molecules/index.ts +++ b/apps/portal/src/components/molecules/index.ts @@ -27,6 +27,7 @@ export * from "./SummaryStats"; export * from "./FilterDropdown"; export * from "./ClearFiltersButton"; export * from "./DetailStatsGrid"; +export { SectionCard } from "./SectionCard"; // Metric display export { MetricCard, MetricCardSkeleton, type MetricCardProps } from "./MetricCard"; diff --git a/apps/portal/src/components/organisms/AppShell/AppShell.tsx b/apps/portal/src/components/organisms/AppShell/AppShell.tsx index d471fcaa..542b4f94 100644 --- a/apps/portal/src/components/organisms/AppShell/AppShell.tsx +++ b/apps/portal/src/components/organisms/AppShell/AppShell.tsx @@ -1,14 +1,12 @@ "use client"; -import { useState, useEffect, useMemo, useRef } from "react"; +import { useState, useEffect, useRef } from "react"; import { usePathname, useRouter } from "next/navigation"; import { useAuthStore, useAuthSession } from "@/features/auth/stores/auth.store"; -import { useActiveSubscriptions } from "@/features/subscriptions/hooks"; import { accountService } from "@/features/account/api/account.api"; import { Sidebar } from "./Sidebar"; import { Header } from "./Header"; -import { computeNavigation, type NavigationItem } from "./navigation"; -import type { Subscription } from "@customer-portal/domain/subscriptions"; +import { baseNavigation, type NavigationItem } from "./navigation"; interface AppShellProps { children: React.ReactNode; @@ -152,12 +150,7 @@ export function AppShell({ children }: AppShellProps) { const router = useRouter(); const { user, isAuthReady, isCheckingAuth } = useAppShellAuth(pathname); const { expandedItems, toggleExpanded } = useSidebarExpansion(pathname); - const activeSubscriptionsQuery = useActiveSubscriptions(); - const activeSubscriptions: Subscription[] = useMemo( - () => activeSubscriptionsQuery.data ?? [], - [activeSubscriptionsQuery.data] - ); - const navigation = useMemo(() => computeNavigation(activeSubscriptions), [activeSubscriptions]); + const navigation = baseNavigation; useEffect(() => { try { diff --git a/apps/portal/src/components/organisms/AppShell/Sidebar.tsx b/apps/portal/src/components/organisms/AppShell/Sidebar.tsx index 91660ad6..24581ad6 100644 --- a/apps/portal/src/components/organisms/AppShell/Sidebar.tsx +++ b/apps/portal/src/components/organisms/AppShell/Sidebar.tsx @@ -67,9 +67,7 @@ export const Sidebar = memo(function Sidebar({
-
- -
+
Assist Solutions

Customer Portal

diff --git a/apps/portal/src/components/organisms/AppShell/navigation.ts b/apps/portal/src/components/organisms/AppShell/navigation.ts index 9e022ab4..81890079 100644 --- a/apps/portal/src/components/organisms/AppShell/navigation.ts +++ b/apps/portal/src/components/organisms/AppShell/navigation.ts @@ -1,4 +1,3 @@ -import type { Subscription } from "@customer-portal/domain/subscriptions"; import { HomeIcon, CreditCardIcon, @@ -40,8 +39,8 @@ export const baseNavigation: NavigationItem[] = [ }, { name: "Subscriptions", + href: "/account/subscriptions", icon: ServerIcon, - children: [{ name: "All Subscriptions", href: "/account/subscriptions" }], }, { name: "Services", href: "/account/services", icon: Squares2X2Icon }, { @@ -59,36 +58,3 @@ export const baseNavigation: NavigationItem[] = [ }, { name: "Log out", href: "#", icon: ArrowRightStartOnRectangleIcon, isLogout: true }, ]; - -export function computeNavigation(activeSubscriptions?: Subscription[]): NavigationItem[] { - const nav: NavigationItem[] = baseNavigation.map(item => ({ - ...item, - children: item.children ? [...item.children] : undefined, - })); - - const subIdx = nav.findIndex(n => n.name === "Subscriptions"); - const currentItem = nav[subIdx]; - if (subIdx >= 0 && currentItem) { - const dynamicChildren = (activeSubscriptions || []).map(sub => ({ - name: truncate(sub.productName || `Subscription ${sub.id}`, 28), - href: `/account/subscriptions/${sub.id}`, - tooltip: sub.productName || `Subscription ${sub.id}`, - })); - - nav[subIdx] = { - name: currentItem.name, - icon: currentItem.icon, - href: currentItem.href, - isLogout: currentItem.isLogout, - section: currentItem.section, - children: [{ name: "All Subscriptions", href: "/account/subscriptions" }, ...dynamicChildren], - }; - } - - return nav; -} - -export function truncate(text: string, max: number): string { - if (text.length <= max) return text; - return text.slice(0, Math.max(0, max - 1)) + "…"; -} diff --git a/apps/portal/src/components/templates/PageLayout/PageLayout.tsx b/apps/portal/src/components/templates/PageLayout/PageLayout.tsx index 4dbda639..c81ecb34 100644 --- a/apps/portal/src/components/templates/PageLayout/PageLayout.tsx +++ b/apps/portal/src/components/templates/PageLayout/PageLayout.tsx @@ -1,20 +1,16 @@ import type { ReactNode } from "react"; import Link from "next/link"; -import { ChevronRightIcon } from "@heroicons/react/24/outline"; +import { ArrowLeftIcon } from "@heroicons/react/24/outline"; import { Skeleton } from "@/components/atoms/loading-skeleton"; import { ErrorState } from "@/components/atoms/error-state"; -export interface BreadcrumbItem { - label: string; - href?: string | undefined; -} - interface PageLayoutProps { icon?: ReactNode | undefined; title: string; description?: string | undefined; actions?: ReactNode | undefined; - breadcrumbs?: BreadcrumbItem[] | undefined; + backLink?: { label: string; href: string } | undefined; + statusPill?: ReactNode | undefined; loading?: boolean | undefined; loadingFallback?: ReactNode | undefined; error?: Error | string | null | undefined; @@ -27,7 +23,8 @@ export function PageLayout({ title, description, actions, - breadcrumbs, + backLink, + statusPill, loading = false, loadingFallback, error = null, @@ -35,42 +32,24 @@ export function PageLayout({ children, }: PageLayoutProps) { return ( -
-
- {/* Breadcrumbs - scrollable on mobile */} - {breadcrumbs && breadcrumbs.length > 0 && ( - - )} +
+ {/* Header band with subtle background */} +
+
+ {/* Back link */} + {backLink && ( +
+ + + {backLink.label} + +
+ )} - {/* Header */} -
-
- {/* Title row */} +
{icon && (
@@ -78,9 +57,12 @@ export function PageLayout({
)}
-

- {title} -

+
+

+ {title} +

+ {statusPill} +
{description && (

{description} @@ -88,25 +70,27 @@ export function PageLayout({ )}

- - {/* Actions - full width on mobile, stacks buttons */} {actions && ( -
+
{actions}
)}
+
- {/* Content with loading and error states */} -
- {renderPageContent({ - loading, - error: error ?? undefined, - children, - onRetry, - loadingFallback, - })} + {/* Content */} +
+
+
+ {renderPageContent({ + loading, + error: error ?? undefined, + children, + onRetry, + loadingFallback, + })} +
diff --git a/apps/portal/src/components/templates/PageLayout/index.ts b/apps/portal/src/components/templates/PageLayout/index.ts index 17045872..c548d8c4 100644 --- a/apps/portal/src/components/templates/PageLayout/index.ts +++ b/apps/portal/src/components/templates/PageLayout/index.ts @@ -1,2 +1 @@ export { PageLayout } from "./PageLayout"; -export type { BreadcrumbItem } from "./PageLayout"; diff --git a/apps/portal/src/components/templates/index.ts b/apps/portal/src/components/templates/index.ts index 42a1c1b1..451d8e6a 100644 --- a/apps/portal/src/components/templates/index.ts +++ b/apps/portal/src/components/templates/index.ts @@ -7,7 +7,6 @@ export { AuthLayout } from "./AuthLayout/AuthLayout"; export type { AuthLayoutProps } from "./AuthLayout/AuthLayout"; export { PageLayout } from "./PageLayout/PageLayout"; -export type { BreadcrumbItem } from "./PageLayout/PageLayout"; export { PublicShell } from "./PublicShell/PublicShell"; export type { PublicShellProps } from "./PublicShell/PublicShell"; diff --git a/apps/portal/src/features/account/views/ProfileContainer.tsx b/apps/portal/src/features/account/views/ProfileContainer.tsx index 1579fa99..9ae614d4 100644 --- a/apps/portal/src/features/account/views/ProfileContainer.tsx +++ b/apps/portal/src/features/account/views/ProfileContainer.tsx @@ -124,25 +124,14 @@ export default function ProfileContainer() { if (isLoading) { return ( - } - title="Profile" - description="Manage your account information" - loading - > + } title="Profile" loading> ); } return ( - } - title="Profile" - description="Manage your account information" - error={error} - onRetry={reload} - > + } title="Profile" error={error} onRetry={reload}> {error && ( {error} diff --git a/apps/portal/src/features/billing/views/InvoiceDetail.tsx b/apps/portal/src/features/billing/views/InvoiceDetail.tsx index 40ee512b..85b86c69 100644 --- a/apps/portal/src/features/billing/views/InvoiceDetail.tsx +++ b/apps/portal/src/features/billing/views/InvoiceDetail.tsx @@ -1,7 +1,6 @@ "use client"; import { useState } from "react"; -import Link from "next/link"; import { useParams } from "next/navigation"; import { LoadingCard, Skeleton } from "@/components/atoms/loading-skeleton"; import { ErrorState } from "@/components/atoms/error-state"; @@ -18,11 +17,7 @@ import { function InvoiceDetailSkeleton() { return ( - } - title="Invoice" - description="Invoice details and actions" - > + } title="Invoice">
@@ -86,18 +81,13 @@ export function InvoiceDetailContainer() { } title="Invoice" - description="Invoice details and actions" + backLink={{ label: "Back to Invoices", href: "/account/billing/invoices" }} > -
- - ← Back to invoices - -
); } @@ -106,12 +96,7 @@ export function InvoiceDetailContainer() { } title={`Invoice #${invoice.id}`} - description="Invoice details and actions" - breadcrumbs={[ - { label: "Billing", href: "/account/billing/invoices" }, - { label: "Invoices", href: "/account/billing/invoices" }, - { label: `#${invoice.id}` }, - ]} + backLink={{ label: "Back to Invoices", href: "/account/billing/invoices" }} >
diff --git a/apps/portal/src/features/billing/views/InvoicesList.tsx b/apps/portal/src/features/billing/views/InvoicesList.tsx index 05d9cdad..20bb713f 100644 --- a/apps/portal/src/features/billing/views/InvoicesList.tsx +++ b/apps/portal/src/features/billing/views/InvoicesList.tsx @@ -6,11 +6,7 @@ import { InvoicesList } from "@/features/billing/components/InvoiceList/InvoiceL export function InvoicesListContainer() { return ( - } - title="Invoices" - description="Manage and view your billing invoices" - > + } title="Invoices"> ); diff --git a/apps/portal/src/features/billing/views/PaymentMethods.tsx b/apps/portal/src/features/billing/views/PaymentMethods.tsx index 2d966734..3febb59d 100644 --- a/apps/portal/src/features/billing/views/PaymentMethods.tsx +++ b/apps/portal/src/features/billing/views/PaymentMethods.tsx @@ -235,11 +235,7 @@ export function PaymentMethodsContainer() { if (combinedError) { return ( - } - title="Payment Methods" - description="Manage your saved payment methods and billing information" - > + } title="Payment Methods"> <> @@ -254,11 +250,7 @@ export function PaymentMethodsContainer() { }; return ( - } - title="Payment Methods" - description="Manage your saved payment methods and billing information" - > + } title="Payment Methods"> ; return ( - } - > + }>
+
@@ -167,7 +167,7 @@ function DashboardContent({ displayName: string; }) { return ( - + ; if (error) { return ( - + -
-

Order Progress

+ } title="Order Progress" tone="info"> {statusDescriptor && ( )} -
+
} title={pageTitle} - description={pageDescription} - breadcrumbs={[{ label: "Orders", href: "/account/orders" }, { label: breadcrumbLabel }]} + backLink={{ label: "Back to Orders", href: "/account/orders" }} > {error &&
{error}
} {isNewOrder && } diff --git a/apps/portal/src/features/orders/views/OrdersList.tsx b/apps/portal/src/features/orders/views/OrdersList.tsx index 5edb9e39..bbf97b14 100644 --- a/apps/portal/src/features/orders/views/OrdersList.tsx +++ b/apps/portal/src/features/orders/views/OrdersList.tsx @@ -224,11 +224,7 @@ export function OrdersListContainer() { const summaryStatsItems = useMemo(() => buildOrderSummaryStats(stats), [stats]); return ( - } - title="My Orders" - description="View and track all your orders" - > + } title="My Orders"> diff --git a/apps/portal/src/features/services/components/internet/configure/InternetConfigureContainer.tsx b/apps/portal/src/features/services/components/internet/configure/InternetConfigureContainer.tsx index d86556a4..c548510e 100644 --- a/apps/portal/src/features/services/components/internet/configure/InternetConfigureContainer.tsx +++ b/apps/portal/src/features/services/components/internet/configure/InternetConfigureContainer.tsx @@ -198,7 +198,7 @@ export function InternetConfigureContainer({ } title="Configure Internet Service" - description="Set up your internet service options" + backLink={{ label: "Back to Services", href: "/account/services" }} >

Plan not found

@@ -228,7 +228,7 @@ export function InternetConfigureContainer({ } title="Configure Internet Service" - description="Set up your internet service options" + backLink={{ label: "Back to Services", href: "/account/services" }} >
diff --git a/apps/portal/src/features/services/components/internet/configure/components/ConfigureLoadingSkeleton.tsx b/apps/portal/src/features/services/components/internet/configure/components/ConfigureLoadingSkeleton.tsx index 0d2c5750..9c59464e 100644 --- a/apps/portal/src/features/services/components/internet/configure/components/ConfigureLoadingSkeleton.tsx +++ b/apps/portal/src/features/services/components/internet/configure/components/ConfigureLoadingSkeleton.tsx @@ -5,11 +5,7 @@ import { ServerIcon } from "@heroicons/react/24/outline"; export function ConfigureLoadingSkeleton() { return ( - } - title="Configure Internet Service" - description="Set up your internet service options" - > + } title="Configure Internet Service">
{/* Back to plans */}
diff --git a/apps/portal/src/features/services/components/sim/SimConfigureView.tsx b/apps/portal/src/features/services/components/sim/SimConfigureView.tsx index cefd1f08..764a0193 100644 --- a/apps/portal/src/features/services/components/sim/SimConfigureView.tsx +++ b/apps/portal/src/features/services/components/sim/SimConfigureView.tsx @@ -154,7 +154,7 @@ export function SimConfigureView(props: Props) { return ( } >
diff --git a/apps/portal/src/features/services/components/sim/configure/LoadingSkeleton.tsx b/apps/portal/src/features/services/components/sim/configure/LoadingSkeleton.tsx index d49355c7..93f32b8f 100644 --- a/apps/portal/src/features/services/components/sim/configure/LoadingSkeleton.tsx +++ b/apps/portal/src/features/services/components/sim/configure/LoadingSkeleton.tsx @@ -5,11 +5,7 @@ import { DevicePhoneMobileIcon } from "@heroicons/react/24/outline"; export function LoadingSkeleton() { return ( - } - > + }>
{/* Header card skeleton */}
diff --git a/apps/portal/src/features/services/components/sim/configure/PlanNotFound.tsx b/apps/portal/src/features/services/components/sim/configure/PlanNotFound.tsx index e5473d0c..da6108d7 100644 --- a/apps/portal/src/features/services/components/sim/configure/PlanNotFound.tsx +++ b/apps/portal/src/features/services/components/sim/configure/PlanNotFound.tsx @@ -8,11 +8,7 @@ export function PlanNotFound() { const servicesBasePath = useServicesBasePath(); return ( - } - > + }>

Plan Not Found

diff --git a/apps/portal/src/features/services/views/AccountServicesOverview.tsx b/apps/portal/src/features/services/views/AccountServicesOverview.tsx index 5cb89dd4..523db599 100644 --- a/apps/portal/src/features/services/views/AccountServicesOverview.tsx +++ b/apps/portal/src/features/services/views/AccountServicesOverview.tsx @@ -11,11 +11,7 @@ import { ServicesOverviewContent } from "@/features/services/components/common/S */ export function AccountServicesOverview() { return ( - } - title="Services" - description="Browse and order connectivity services" - > + } title="Services"> ); diff --git a/apps/portal/src/features/subscriptions/components/CancellationFlow/CancellationFlow.tsx b/apps/portal/src/features/subscriptions/components/CancellationFlow/CancellationFlow.tsx index dc5a8cf8..94911f33 100644 --- a/apps/portal/src/features/subscriptions/components/CancellationFlow/CancellationFlow.tsx +++ b/apps/portal/src/features/subscriptions/components/CancellationFlow/CancellationFlow.tsx @@ -1,11 +1,10 @@ "use client"; -import Link from "next/link"; import { useState, type ReactNode } from "react"; -import { PageLayout, type BreadcrumbItem } from "@/components/templates/PageLayout"; +import { PageLayout } from "@/components/templates/PageLayout"; import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner"; import { Button } from "@/components/atoms"; -import { ArrowLeftIcon, CheckIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline"; +import { CheckIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline"; // ============================================================================ // Types @@ -23,10 +22,6 @@ export interface CancellationFlowProps { icon: ReactNode; /** Page title */ title: string; - /** Page description / subtitle */ - description: string; - /** Breadcrumb items */ - breadcrumbs: BreadcrumbItem[]; /** Back link URL */ backHref: string; /** Back link label */ @@ -511,8 +506,6 @@ function StepRouter(props: StepRouterProps) { export function CancellationFlow({ icon, title, - description, - breadcrumbs, backHref, backLabel, availableMonths, @@ -577,20 +570,12 @@ export function CancellationFlow({
- - - {backLabel} - {warningBanner} diff --git a/apps/portal/src/features/subscriptions/components/SubscriptionGridCard.tsx b/apps/portal/src/features/subscriptions/components/SubscriptionGridCard.tsx index 894526ec..8e6c429c 100644 --- a/apps/portal/src/features/subscriptions/components/SubscriptionGridCard.tsx +++ b/apps/portal/src/features/subscriptions/components/SubscriptionGridCard.tsx @@ -34,12 +34,14 @@ export function SubscriptionGridCard({ subscription, className }: SubscriptionGr const { formatCurrency } = useFormatCurrency(); const statusIndicator = mapSubscriptionStatus(subscription.status); const cycleLabel = getBillingCycleLabel(subscription.cycle); + const isInactive = ["Completed", "Cancelled", "Terminated"].includes(subscription.status); return ( +
diff --git a/apps/portal/src/features/subscriptions/views/CancelSubscription.tsx b/apps/portal/src/features/subscriptions/views/CancelSubscription.tsx index ff147b94..843ec2fe 100644 --- a/apps/portal/src/features/subscriptions/views/CancelSubscription.tsx +++ b/apps/portal/src/features/subscriptions/views/CancelSubscription.tsx @@ -43,12 +43,7 @@ function CancellationPendingView({
@@ -236,12 +231,6 @@ function CancellationFlowView({ } title="Cancel Subscription" - description="Loading cancellation information..." - breadcrumbs={[{ label: "Subscriptions", href: SUBSCRIPTIONS_HREF }, { label: "Cancel" }]} + backLink={{ label: "Back to Subscriptions", href: SUBSCRIPTIONS_HREF }} loading={state.loading} error={state.error} > diff --git a/apps/portal/src/features/subscriptions/views/SimCallHistory.tsx b/apps/portal/src/features/subscriptions/views/SimCallHistory.tsx index 6b9c4227..e2564ce2 100644 --- a/apps/portal/src/features/subscriptions/views/SimCallHistory.tsx +++ b/apps/portal/src/features/subscriptions/views/SimCallHistory.tsx @@ -128,7 +128,7 @@ export function SimCallHistoryContainer(): React.ReactElement { } title="Call & SMS History" - description="View your call and SMS records" + backLink={{ label: "Back to Subscription", href: `/account/subscriptions/${subscriptionId}` }} >
diff --git a/apps/portal/src/features/subscriptions/views/SimChangePlan.tsx b/apps/portal/src/features/subscriptions/views/SimChangePlan.tsx index aeaada0d..58f712c3 100644 --- a/apps/portal/src/features/subscriptions/views/SimChangePlan.tsx +++ b/apps/portal/src/features/subscriptions/views/SimChangePlan.tsx @@ -238,7 +238,7 @@ export function SimChangePlanContainer() { } title="Change Plan" - description="Switch to a different data plan" + backLink={{ label: "Back to Subscription", href: `/account/subscriptions/${subscriptionId}` }} >
diff --git a/apps/portal/src/features/subscriptions/views/SimReissue.tsx b/apps/portal/src/features/subscriptions/views/SimReissue.tsx index 72530d13..0e4ee321 100644 --- a/apps/portal/src/features/subscriptions/views/SimReissue.tsx +++ b/apps/portal/src/features/subscriptions/views/SimReissue.tsx @@ -331,7 +331,7 @@ export function SimReissueContainer() { } title="Reissue SIM" - description="Request a replacement SIM card" + backLink={{ label: "Back to Subscription", href: `/account/subscriptions/${subscriptionId}` }} >
diff --git a/apps/portal/src/features/subscriptions/views/SimTopUp.tsx b/apps/portal/src/features/subscriptions/views/SimTopUp.tsx index 732abf84..84023015 100644 --- a/apps/portal/src/features/subscriptions/views/SimTopUp.tsx +++ b/apps/portal/src/features/subscriptions/views/SimTopUp.tsx @@ -100,7 +100,7 @@ export function SimTopUpContainer() { } title="Top Up Data" - description="Add data to your SIM" + backLink={{ label: "Back to Subscription", href: `/account/subscriptions/${subscriptionId}` }} >
diff --git a/apps/portal/src/features/subscriptions/views/SubscriptionDetail.tsx b/apps/portal/src/features/subscriptions/views/SubscriptionDetail.tsx index 956f44e4..a21c01df 100644 --- a/apps/portal/src/features/subscriptions/views/SubscriptionDetail.tsx +++ b/apps/portal/src/features/subscriptions/views/SubscriptionDetail.tsx @@ -13,6 +13,7 @@ import { import { useSubscription } from "@/features/subscriptions/hooks"; import { Formatting } from "@customer-portal/domain/toolkit"; import { PageLayout } from "@/components/templates/PageLayout"; +import { SectionCard } from "@/components/molecules/SectionCard"; import { StatusPill } from "@/components/atoms/status-pill"; import { SubscriptionDetailStatsSkeleton } from "@/components/atoms/loading-skeleton"; import { formatIsoDate, cn } from "@/shared/utils"; @@ -197,23 +198,24 @@ function SubscriptionDetailContent({ {isSim && } {activeTab === "sim" && isSim && } {activeTab === "overview" && ( -
-
-
- -

Billing

-
+ } + title="Billing Information" + subtitle="Payment and invoices" + tone="primary" + actions={ - View all invoices → + View Invoices -
-

+ } + > +

Invoices and payment history are available on the billing page.

-
+ )}
); @@ -231,11 +233,7 @@ export function SubscriptionDetailContainer() { } title="Subscription" - description="Loading subscription details..." - breadcrumbs={[ - { label: "Subscriptions", href: "/account/subscriptions" }, - { label: "Subscription" }, - ]} + backLink={{ label: "Back to Subscriptions", href: "/account/subscriptions" }} >
@@ -255,10 +253,7 @@ export function SubscriptionDetailContainer() { icon={} title={subscription?.productName ?? "Subscription"} actions={headerActions} - breadcrumbs={[ - { label: "Subscriptions", href: "/account/subscriptions" }, - { label: subscription?.productName ?? "Subscription" }, - ]} + backLink={{ label: "Back to Subscriptions", href: "/account/subscriptions" }} error={getPageError(error)} > {subscription ? ( diff --git a/apps/portal/src/features/subscriptions/views/SubscriptionsList.tsx b/apps/portal/src/features/subscriptions/views/SubscriptionsList.tsx index 25302b68..e7f285cf 100644 --- a/apps/portal/src/features/subscriptions/views/SubscriptionsList.tsx +++ b/apps/portal/src/features/subscriptions/views/SubscriptionsList.tsx @@ -146,11 +146,7 @@ export function SubscriptionsListContainer() { if (showLoading || error) { return ( - } - title="Subscriptions" - description="Manage your active subscriptions" - > + } title="Subscriptions">
@@ -165,7 +161,6 @@ export function SubscriptionsListContainer() { } title="Subscriptions" - description="Manage your active subscriptions" actions={ + ) : undefined +} +``` + +Remove the old inline cancel button from the body. + +**Step 3: Use SectionCard for billing section** + +```tsx +import { SectionCard } from "@/components/molecules/SectionCard"; + +// Replace the billing card with: +} + title="Billing Information" + subtitle="Payment and invoices" + tone="primary" + actions={ + + View Invoices + + } +> + {/* billing content */} +; +``` + +**Step 4: Remove description from PageLayout** + +Drop `description` prop from PageLayout usage. + +**Step 5: Verify** + +Run: `pnpm type-check` + +**Step 6: Commit** + +``` +style: update subscription detail with backLink and SectionCard +``` + +--- + +### Task 7: Update Orders Views + +**Files:** + +- Modify: `apps/portal/src/features/orders/views/OrdersList.tsx` +- Modify: `apps/portal/src/features/orders/views/OrderDetail.tsx` + +**Step 1: OrdersList — remove description, standardize card borders** + +Remove `description` prop from PageLayout. Change any `border-border/60` to `border-border`. Replace inline status badge classes with `StatusPill` component. + +**Step 2: OrderDetail — replace breadcrumbs with backLink** + +```tsx +// Before +breadcrumbs={[{ label: "Orders", href: "/account/orders" }, { label: `Order #${id}` }]} + +// After +backLink={{ label: "Back to Orders", href: "/account/orders" }} +``` + +Remove `description` prop. Use `SectionCard` for order item sections. + +**Step 3: Remove any custom title sizes** + +If OrderDetail has its own `text-2xl` title styling, remove it — let PageLayout handle the title. + +**Step 4: Verify** + +Run: `pnpm type-check` + +**Step 5: Commit** + +``` +style: update order views with unified patterns +``` + +--- + +### Task 8: Update Billing Views + +**Files:** + +- Modify: `apps/portal/src/features/billing/views/InvoicesList.tsx` +- Modify: `apps/portal/src/features/billing/views/InvoiceDetail.tsx` +- Modify: `apps/portal/src/features/billing/views/PaymentMethods.tsx` + +**Step 1: InvoicesList — remove description** + +Drop `description` prop from PageLayout. + +**Step 2: InvoiceDetail — replace breadcrumbs with backLink** + +```tsx +backLink={{ label: "Back to Invoices", href: "/account/billing/invoices" }} +``` + +Remove `description`. Use `SectionCard` for invoice detail sections where appropriate. + +**Step 3: PaymentMethods — remove description** + +Drop `description` prop. + +**Step 4: Verify** + +Run: `pnpm type-check` + +**Step 5: Commit** + +``` +style: update billing views with unified patterns +``` + +--- + +### Task 9: Update Support Views + +**Files:** + +- Modify: `apps/portal/src/features/support/views/SupportCasesView.tsx` +- Modify: `apps/portal/src/features/support/views/SupportCaseDetailView.tsx` +- Modify: `apps/portal/src/features/support/views/NewSupportCaseView.tsx` + +**Step 1: SupportCasesView — remove description, fix inline badges** + +Remove `description` and `breadcrumbs` props from PageLayout. Replace inline badge classes (`inline-flex text-xs px-2 py-0.5 rounded font-medium`) with `` component. + +**Step 2: SupportCaseDetailView — replace breadcrumbs with backLink** + +```tsx +backLink={{ label: "Back to Cases", href: "/account/support" }} +``` + +Remove `description`. Use `SectionCard` for conversation and meta sections. + +**Step 3: NewSupportCaseView — replace breadcrumbs with backLink** + +```tsx +backLink={{ label: "Back to Cases", href: "/account/support" }} +``` + +Remove `description`. + +**Step 4: Verify** + +Run: `pnpm type-check` + +**Step 5: Commit** + +``` +style: update support views with unified patterns +``` + +--- + +### Task 10: Update Remaining Views + +**Files:** + +- Modify: `apps/portal/src/features/dashboard/views/DashboardView.tsx` +- Modify: `apps/portal/src/features/services/views/AccountServicesOverview.tsx` +- Modify: `apps/portal/src/features/account/views/ProfileContainer.tsx` +- Modify: `apps/portal/src/features/verification/views/ResidenceCardVerificationSettingsView.tsx` +- Modify: `apps/portal/src/features/subscriptions/views/SimReissue.tsx` +- Modify: `apps/portal/src/features/subscriptions/views/SimTopUp.tsx` +- Modify: `apps/portal/src/features/subscriptions/views/SimChangePlan.tsx` +- Modify: `apps/portal/src/features/subscriptions/views/SimCallHistory.tsx` +- Modify: `apps/portal/src/features/subscriptions/views/CancelSubscription.tsx` +- Modify: `apps/portal/src/features/subscriptions/components/CancellationFlow/CancellationFlow.tsx` +- Modify: `apps/portal/src/features/checkout/components/AccountCheckoutContainer.tsx` +- Modify: `apps/portal/src/features/services/components/sim/SimConfigureView.tsx` +- Modify: `apps/portal/src/features/services/components/internet/configure/InternetConfigureContainer.tsx` + +**Step 1: Remove description from all remaining PageLayout usages** + +For every file listed above, remove the `description` prop from ``. + +**Step 2: Replace breadcrumbs with backLink where applicable** + +- `ResidenceCardVerificationSettingsView.tsx`: `backLink={{ label: "Back to Settings", href: "/account/settings" }}` +- `CancelSubscription.tsx` / `CancellationFlow.tsx`: `backLink={{ label: "Back to Subscription", href: "/account/subscriptions/{id}" }}` +- SIM views (Reissue, TopUp, ChangePlan, CallHistory): `backLink={{ label: "Back to Subscription", href: "/account/subscriptions/{id}" }}` + +**Step 3: Verify** + +Run: `pnpm type-check` +Expected: No errors. All `breadcrumbs` prop usages should be gone. + +**Step 4: Commit** + +``` +style: remove descriptions and breadcrumbs from all remaining views +``` + +--- + +### Task 11: Fix Domain Build & Clean Up + +**Files:** + +- Modify: `apps/portal/src/features/subscriptions/components/SubscriptionGridCard.tsx` (if import still broken) +- Modify: `apps/portal/src/components/templates/PageLayout/PageLayout.tsx` (remove dead BreadcrumbItem type if not already) + +**Step 1: Build domain package** + +Run: `pnpm domain:build` +Expected: Successful build, resolves `@customer-portal/domain/subscriptions` import errors. + +**Step 2: Remove BreadcrumbItem from PageLayout exports** + +If `BreadcrumbItem` is still exported, remove it. Check if any file imports it: + +Run: `grep -r "BreadcrumbItem" apps/portal/src/` + +Remove any remaining imports. + +**Step 3: Full type check** + +Run: `pnpm type-check` +Expected: Clean — no errors. + +**Step 4: Lint check** + +Run: `pnpm lint` +Expected: Clean or only pre-existing warnings. + +**Step 5: Commit** + +``` +chore: fix domain build and remove dead breadcrumb types +``` + +--- + +### Task 12: Final Visual Verification + +**Step 1: Start dev server (with user permission)** + +Run: `pnpm --filter @customer-portal/portal dev` + +**Step 2: Verify each page visually** + +Check these pages match the design: + +- [ ] Sidebar: deeper blue, white logo, Subscriptions as direct link +- [ ] Dashboard (`/account`): muted header bg, no description +- [ ] Subscriptions list (`/account/subscriptions`): muted header, metrics row, unified content card, inactive subs dimmed +- [ ] Subscription detail (`/account/subscriptions/{id}`): back link, header with actions, SectionCard for billing +- [ ] Orders list (`/account/orders`): consistent with subscriptions list pattern +- [ ] Order detail (`/account/orders/{id}`): back link, SectionCards +- [ ] Invoices (`/account/billing/invoices`): consistent list pattern +- [ ] Invoice detail: back link, consistent +- [ ] Support cases (`/account/support`): StatusPill badges, no inline badges +- [ ] Support detail: back link, consistent +- [ ] Settings (`/account/settings`): muted header + +**Step 3: Fix any visual issues found** + +Address spacing, alignment, or color issues discovered during review. + +**Step 4: Final commit** + +``` +style: portal UI cleanup — unified page patterns and brand alignment +```