# 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
<div
  key={activeTab}
  className={cn(
    "space-y-8",
    slideDirection.current === "left"
      ? "animate-slide-fade-left"
      : "animate-slide-fade-right"
  )}
>
```

**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 (`<div id="plans" className="min-h-[280px]">` at line 375), add `overflow-hidden` to prevent horizontal scrollbar during slide:

```tsx
<div id="plans" className="min-h-[280px] overflow-hidden">
```

**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 (
    <div
      className={cn(
        "group relative rounded-xl overflow-hidden transition-all duration-200",
        "hover:-translate-y-0.5 hover:shadow-lg",
        isFamily
          ? "border border-success/30 bg-gradient-to-br from-emerald-50/60 to-card dark:from-emerald-950/20 dark:to-card hover:border-success"
          : "border border-border bg-gradient-to-br from-sky-50/40 to-card dark:from-sky-950/15 dark:to-card hover:border-primary/50"
      )}
    >
      <div className="p-5">
        {isFamily && (
          <div className="flex items-center gap-1.5 mb-3">
            <Users className="w-3.5 h-3.5 text-success" />
            <span className="text-[10px] font-semibold text-success uppercase tracking-wider">
              Family Discount
            </span>
          </div>
        )}

        {/* Data size - hero element */}
        <div className="flex items-center gap-2.5 mb-4">
          <div
            className={cn(
              "w-10 h-10 rounded-xl flex items-center justify-center",
              isFamily ? "bg-success/10" : "bg-primary/8"
            )}
          >
            <Signal className={cn("w-5 h-5", isFamily ? "text-success" : "text-primary")} />
          </div>
          <span className="text-2xl font-bold text-foreground tracking-tight">
            {plan.simDataSize}
          </span>
        </div>

        {/* Price - prominent */}
        <div className="mb-1">
          <div className="flex items-baseline gap-0.5">
            <span className="text-xl font-bold text-foreground">
              ¥{displayPrice.toLocaleString()}
            </span>
            <span className="text-xs text-muted-foreground">/mo</span>
          </div>
          {isFamily && (
            <div className="text-[10px] text-success font-medium mt-0.5">Discounted price</div>
          )}
        </div>

        {/* Plan name */}
        <p className="text-xs text-muted-foreground mb-4 line-clamp-2 min-h-[2rem]">{plan.name}</p>

        {/* CTA - filled button */}
        <Button
          className="w-full"
          variant={isFamily ? "success" : "default"}
          size="sm"
          onClick={() => onSelect(plan.sku)}
          rightIcon={<ArrowRight className="w-3.5 h-3.5" />}
        >
          Select Plan
        </Button>
      </div>
    </div>
  );
}
```

**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"
```
