# Internet & SIM Service Page Redesign - Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Redesign the public internet page to use a unified card with offering selector, and improve SIM page tab transitions + plan card design. **Architecture:** Two independent UI refactors. Internet page merges ConsolidatedInternetCard + AvailablePlansSection into one component with a segmented control for offering type. SIM page adds direction-aware slide transitions and richer plan cards. **Tech Stack:** React 19, Tailwind CSS, shadcn/ui, lucide-react icons --- ## Task 1: Internet - Build Unified Internet Card with Offering Selector **Files:** - Modify: `apps/portal/src/features/services/views/PublicInternetPlans.tsx` **Context:** This file currently has two separate sections: 1. `ConsolidatedInternetCard` (lines 59-209) - shows price range + 3 tier cards 2. `AvailablePlansSection` (lines 653-731) - 3 expandable offering headers that open tier details below 3. Helper components: `PlanCardHeader` (266-340), `ExpandedTierDetails` (345-465), `MobilePlanCard` (470-648) The goal is to merge these into ONE card with a segmented offering type selector. **Step 1: Replace ConsolidatedInternetCard with UnifiedInternetCard** Delete the `ConsolidatedInternetCard` component (lines 59-209) and replace with a new `UnifiedInternetCard` component that: 1. Accepts both `consolidatedPlanData` (for "All Plans" price ranges) AND `plansByOffering` (for per-offering exact prices) 2. Has internal state: `selectedOffering: "all" | "home10g" | "home1g" | "apartment"` 3. Header section: - Same Wifi icon + "NTT Fiber Internet" title - Price display that updates based on `selectedOffering`: - "all": shows full min~max range - specific offering: shows that offering's min~max (will often be exact price) 4. Segmented control below header: - Pills: "All Plans" | "Home 10G" | "Home 1G" | "Apartment" - Active pill: `bg-card text-foreground shadow-sm` - Inactive: `text-muted-foreground hover:text-foreground` - Container: `bg-muted/60 p-0.5 rounded-lg border border-border/60` (matches SIM tab style) - On mobile: horizontally scrollable with `overflow-x-auto` - Each pill shows the offering icon (Home/Building) and optional speed badge 5. Tier cards grid (same 3 columns: Silver, Gold, Platinum): - When "All Plans": show price ranges per tier (current behavior from consolidated card) - When specific offering selected: show exact prices for that offering's tiers - Wrap the grid in a container with `transition-opacity duration-200` for smooth price updates 6. Footer: same setup fee + CTA button **Step 2: Remove obsolete components** Delete from `PublicInternetPlans.tsx`: - `PlanCardHeader` component (lines 266-340) - `ExpandedTierDetails` component (lines 345-465) - `MobilePlanCard` component (lines 470-648) - `AvailablePlansSection` component (lines 653-731) **Step 3: Update PublicInternetPlansContent render** In the `PublicInternetPlansContent` component's return JSX (starts line 961): - Replace the `ConsolidatedInternetCard` usage (lines 973-987) with `UnifiedInternetCard` - Pass it both `consolidatedPlanData` and `plansByOffering` - Remove the `AvailablePlansSection` usage (lines 989-995) entirely **Step 4: Verify and commit** ```bash cd /home/barsa/projects/customer_portal/customer-portal pnpm type-check pnpm lint ``` Expected: No type errors or lint issues. ```bash git add apps/portal/src/features/services/views/PublicInternetPlans.tsx git commit -m "feat: unified internet card with offering type selector" ``` --- ## Task 2: Internet - Clean Up Unused PublicOfferingCard **Files:** - Check: `apps/portal/src/features/services/components/internet/PublicOfferingCard.tsx` **Context:** After Task 1, the `TierInfo` type is still imported from `PublicOfferingCard.tsx` in `PublicInternetPlans.tsx`. The `PublicOfferingCard` component itself is no longer used. **Step 1: Move TierInfo type inline** In `PublicInternetPlans.tsx`, the import `type { TierInfo } from "@/features/services/components/internet/PublicOfferingCard"` needs to change. Define `TierInfo` directly in `PublicInternetPlans.tsx`: ```typescript interface TierInfo { tier: "Silver" | "Gold" | "Platinum"; monthlyPrice: number; maxMonthlyPrice?: number; description: string; features: string[]; pricingNote?: string; } ``` Remove the import of `TierInfo` from `PublicOfferingCard`. **Step 2: Check if PublicOfferingCard is imported anywhere else** Search for all imports of `PublicOfferingCard` across the codebase. If no other files import it, it can be deleted. If other files import only `TierInfo`, move the type to a shared location or inline it. **Step 3: Verify and commit** ```bash pnpm type-check pnpm lint git add -A git commit -m "refactor: inline TierInfo type, remove unused PublicOfferingCard" ``` --- ## Task 3: SIM - Direction-Aware Tab Transitions **Files:** - Modify: `apps/portal/src/features/services/components/sim/SimPlansContent.tsx` **Context:** The tab switcher is at lines 348-372. The plans grid is at lines 374-410. Currently uses `animate-in fade-in duration-300` on the grid wrapper (line 377). The `SIM_TABS` array (lines 86-108) defines tab order: data-voice (0), data-only (1), voice-only (2). **Step 1: Add transition state tracking** In `SimPlansContent` component, add state to track slide direction: ```typescript import { useMemo, useRef } from "react"; // Inside SimPlansContent: const prevTabRef = useRef(activeTab); const slideDirection = useRef<"left" | "right">("left"); // Update direction when tab changes if (prevTabRef.current !== activeTab) { const tabKeys = SIM_TABS.map(t => t.key); const prevIndex = tabKeys.indexOf(prevTabRef.current); const nextIndex = tabKeys.indexOf(activeTab); slideDirection.current = nextIndex > prevIndex ? "left" : "right"; prevTabRef.current = activeTab; } ``` **Step 2: Replace the grid animation** Replace the current `animate-in fade-in duration-300` on line 377 with a keyed wrapper that triggers CSS animation: ```tsx
``` **Step 3: Add the CSS animations** Check if `tailwind.config.ts` is in `apps/portal/` and add custom keyframes: ```javascript // In tailwind.config.ts extend.keyframes: "slide-fade-left": { "0%": { opacity: "0", transform: "translateX(24px)" }, "100%": { opacity: "1", transform: "translateX(0)" }, }, "slide-fade-right": { "0%": { opacity: "0", transform: "translateX(-24px)" }, "100%": { opacity: "1", transform: "translateX(0)" }, }, // In extend.animation: "slide-fade-left": "slide-fade-left 300ms ease-out", "slide-fade-right": "slide-fade-right 300ms ease-out", ``` **Step 4: Add overflow-hidden to the container** On the plans grid container (`
` at line 375), add `overflow-hidden` to prevent horizontal scrollbar during slide: ```tsx
``` **Step 5: Verify and commit** ```bash pnpm type-check pnpm lint git add -A git commit -m "feat: direction-aware slide transitions for SIM tab switching" ``` --- ## Task 4: SIM - Redesign Plan Cards **Files:** - Modify: `apps/portal/src/features/services/components/sim/SimPlansContent.tsx` **Context:** `SimPlanCardCompact` is defined at lines 152-229. Currently has: - Thin 0.5px top accent stripe (line 173-180) - Small signal icon (w-4 h-4) in a 9x9 box + data size in text-lg (line 193-203) - Small pricing via CardPricing component (line 207) - Plan name in text-xs (line 214) - Outline "Select Plan" button (line 217-225) **Step 1: Redesign the card** Replace the entire `SimPlanCardCompact` function (lines 152-229) with an enhanced version: Key changes: 1. **Remove thin accent stripe** - replace with subtle gradient overlay at top 2. **Data size hero**: Bump from `text-lg` to `text-2xl font-bold`, make it the visual centerpiece 3. **Signal icon**: Keep but make slightly larger (w-5 h-5) in a bigger container 4. **Price**: Show directly as `text-xl font-bold` with yen symbol, more prominent than current CardPricing 5. **Plan name**: Keep as subtitle in `text-xs text-muted-foreground` 6. **Hover effect**: Add `hover:-translate-y-0.5 hover:shadow-lg` for lift effect 7. **Button**: Change from `variant="outline"` to filled `variant="default"` for regular, `variant="success"` for family 8. **Background**: Add subtle gradient - `bg-gradient-to-br from-sky-50/50 to-transparent` for regular (dark mode: `dark:from-sky-950/20`), emerald variant for family 9. **Border**: Slightly thicker on hover with primary color ```tsx function SimPlanCardCompact({ plan, isFamily, onSelect, }: { plan: SimCatalogProduct; isFamily?: boolean; onSelect: (sku: string) => void; }) { const displayPrice = plan.monthlyPrice ?? plan.unitPrice ?? plan.oneTimePrice ?? 0; return (
{isFamily && (
Family Discount
)} {/* Data size - hero element */}
{plan.simDataSize}
{/* Price - prominent */}
¥{displayPrice.toLocaleString()} /mo
{isFamily && (
Discounted price
)}
{/* Plan name */}

{plan.name}

{/* CTA - filled button */}
); } ``` **Step 2: Verify the Button `success` variant exists** Check `apps/portal/src/components/atoms/button.tsx` for available variants. If `success` doesn't exist, use `variant="default"` with a custom className for family cards: `className="w-full bg-success hover:bg-success/90 text-success-foreground"`. **Step 3: Verify and commit** ```bash pnpm type-check pnpm lint git add apps/portal/src/features/services/components/sim/SimPlansContent.tsx git commit -m "feat: redesign SIM plan cards with improved visual hierarchy" ``` --- ## Task 5: Visual QA and Polish **Step 1: Run dev server and check both pages** ```bash pnpm --filter @customer-portal/portal dev ``` Check in browser: - `/services/internet` - Verify unified card, offering selector, price updates - `/services/sim` - Verify tab transitions, card design - Test mobile responsiveness (Chrome DevTools responsive mode) - Test dark mode if supported **Step 2: Fix any visual issues found during QA** Common things to check: - Segmented control alignment on mobile - Price animation smoothness - Slide transition not causing layout shift - Card hover effects working - Dark mode color contrast **Step 3: Final commit** ```bash git add -A git commit -m "style: polish internet and SIM service page redesign" ```