Assist_Design/docs/plans/2026-03-04-public-pages-restructuring-plan.md
barsa 5a66adb7e6 refactor: update landing page components and styles
- Removed obsolete components such as AnimatedBackground, FloatingGlassCard, TrustBadge, TrustIndicators, and ValuePropCard to streamline the landing page.
- Enhanced existing components like CTABanner and HeroSection with improved accessibility and styling.
- Updated global CSS to introduce new line-height tokens and improved typography.
- Refactored the PublicContactView to focus on a streamlined contact form and sidebar information.
- Improved the ServicesGrid component to utilize a new data structure for landing services.
- Enhanced button components with new variants for better UI consistency.
2026-03-04 13:42:03 +09:00

884 lines
30 KiB
Markdown

# Public Pages Restructuring Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Restructure public pages — update homepage hero, extract reusable ContactForm, clean up Contact page (form + sidebar), create Support page (knowledge base categories, remote tools, FAQ, contact form fallback).
**Architecture:** Extract shared ContactForm from PublicContactView into `features/support/components/`. Rewrite PublicContactView as focused two-column contact page. Rewrite PublicSupportView as self-service hub. Update HeroSection text. Add `/support` route.
**Tech Stack:** Next.js 15, React 19, Tailwind CSS, shadcn/ui atoms, Zod validation, lucide-react icons
---
### Task 1: Update Homepage Hero Text
**Files:**
- Modify: `apps/portal/src/features/landing-page/components/HeroSection.tsx:42-49`
**Step 1: Update the hero heading and subtitle**
Change lines 42-49 in `HeroSection.tsx`:
```tsx
// OLD:
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold leading-tight text-foreground">
<span className="block">English IT Support</span>
<span className="block text-primary mt-2">for Expats in Japan</span>
</h1>
<p className="mt-6 text-base sm:text-lg text-muted-foreground leading-relaxed font-semibold max-w-2xl mx-auto">
No Japanese required. Get reliable internet, mobile, and VPN services with full English
support. Serving expats and international businesses for over 20 years.
</p>
// NEW:
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold leading-tight text-foreground">
<span className="block">A One Stop Solution</span>
<span className="block text-primary mt-2">for Your IT Needs</span>
</h1>
<p className="mt-6 text-base sm:text-lg text-muted-foreground leading-relaxed font-semibold max-w-2xl mx-auto">
From internet and mobile to VPN and on-site tech support we handle it all in English so you don&apos;t have to.
</p>
```
**Step 2: Verify no lint errors**
Run: `pnpm lint --filter @customer-portal/portal`
**Step 3: Commit**
```
feat: update homepage hero text to "A One Stop Solution for Your IT Needs"
```
---
### Task 2: Extract Reusable ContactForm Component
**Files:**
- Create: `apps/portal/src/features/support/components/ContactForm.tsx`
- Create: `apps/portal/src/features/support/components/index.ts`
**Step 1: Create the ContactForm component**
Create `apps/portal/src/features/support/components/ContactForm.tsx`:
```tsx
"use client";
import { useState, useCallback } from "react";
import Link from "next/link";
import { Button, Input } from "@/components/atoms";
import { FormField } from "@/components/molecules/FormField/FormField";
import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner";
import { useZodForm } from "@/shared/hooks";
import { CheckCircle } from "lucide-react";
import {
publicContactRequestSchema,
type PublicContactRequest,
} from "@customer-portal/domain/support";
import { apiClient, ApiError, isApiError } from "@/core/api";
import { cn } from "@/shared/utils";
interface ContactFormProps {
className?: string;
}
export function ContactForm({ className }: ContactFormProps) {
const [isSubmitted, setIsSubmitted] = useState(false);
const [submitError, setSubmitError] = useState<string | null>(null);
const handleSubmit = useCallback(async (data: PublicContactRequest) => {
setSubmitError(null);
try {
await apiClient.POST("/api/support/contact", { body: data });
setIsSubmitted(true);
} catch (error) {
if (isApiError(error)) {
setSubmitError(error.message || "Failed to send message");
return;
}
if (error instanceof ApiError) {
setSubmitError(error.message || "Failed to send message");
return;
}
setSubmitError(error instanceof Error ? error.message : "Failed to send message");
}
}, []);
const form = useZodForm<PublicContactRequest>({
schema: publicContactRequestSchema,
initialValues: {
name: "",
email: "",
phone: "",
subject: "",
message: "",
},
onSubmit: handleSubmit,
});
if (isSubmitted) {
return (
<div className={cn("text-center py-12", className)}>
<div className="w-16 h-16 bg-success/10 rounded-full flex items-center justify-center mx-auto mb-4">
<CheckCircle className="h-8 w-8 text-success" />
</div>
<h3 className="text-2xl font-bold text-foreground mb-2">Message Sent!</h3>
<p className="text-muted-foreground mb-6">
Thank you for contacting us. We&apos;ll get back to you within 24 hours.
</p>
<div className="flex gap-3 justify-center">
<Button as="a" href="/" variant="outline" size="sm">
Back to Home
</Button>
<Button as="a" href="/services" size="sm">
Browse Services
</Button>
</div>
</div>
);
}
return (
<div className={cn("bg-white rounded-2xl border border-border/60 p-6", className)}>
{submitError && (
<AlertBanner variant="error" title="Error" className="mb-6">
{submitError}
</AlertBanner>
)}
<form onSubmit={event => void form.handleSubmit(event)} className="space-y-5">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-5">
<FormField
label="Name"
error={form.touched["name"] ? form.errors["name"] : undefined}
required
>
<Input
value={form.values.name}
onChange={e => form.setValue("name", e.target.value)}
onBlur={() => form.setTouchedField("name")}
placeholder="Your name"
className="bg-muted/20"
/>
</FormField>
<FormField
label="Email"
error={form.touched["email"] ? form.errors["email"] : undefined}
required
>
<Input
type="email"
value={form.values.email}
onChange={e => form.setValue("email", e.target.value)}
onBlur={() => form.setTouchedField("email")}
placeholder="your@email.com"
className="bg-muted/20"
/>
</FormField>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-5">
<FormField label="Phone" error={form.touched["phone"] ? form.errors["phone"] : undefined}>
<Input
value={form.values.phone ?? ""}
onChange={e => form.setValue("phone", e.target.value)}
onBlur={() => form.setTouchedField("phone")}
placeholder="+81 90-1234-5678"
className="bg-muted/20"
/>
</FormField>
<FormField
label="Subject"
error={form.touched["subject"] ? form.errors["subject"] : undefined}
required
>
<Input
value={form.values.subject}
onChange={e => form.setValue("subject", e.target.value)}
onBlur={() => form.setTouchedField("subject")}
placeholder="How can we help?"
className="bg-muted/20"
/>
</FormField>
</div>
<FormField
label="Message"
error={form.touched["message"] ? form.errors["message"] : undefined}
required
>
<textarea
className="flex min-h-[120px] w-full rounded-lg border border-input bg-muted/20 px-4 py-3 text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:border-transparent disabled:cursor-not-allowed disabled:opacity-50 transition-colors resize-y text-sm"
value={form.values.message}
onChange={e => form.setValue("message", e.target.value)}
onBlur={() => form.setTouchedField("message")}
placeholder="Tell us more about your inquiry..."
rows={4}
/>
</FormField>
<Button
type="submit"
className="w-full"
size="lg"
disabled={form.isSubmitting}
isLoading={form.isSubmitting}
loadingText="Sending..."
>
Send Message
</Button>
</form>
<p className="text-xs text-muted-foreground mt-4 pt-4 border-t border-border/60">
By submitting, you agree to our{" "}
<Link href="#" className="text-primary hover:underline">
Privacy Policy
</Link>
. We typically respond within 24 hours.
</p>
</div>
);
}
```
**Step 2: Create barrel export**
Create `apps/portal/src/features/support/components/index.ts`:
```ts
export { ContactForm } from "./ContactForm";
```
**Step 3: Verify no lint errors**
Run: `pnpm lint --filter @customer-portal/portal`
**Step 4: Commit**
```
feat: extract reusable ContactForm component from PublicContactView
```
---
### Task 3: Rewrite Contact Page (Two-Column Layout)
**Files:**
- Modify: `apps/portal/src/features/support/views/PublicContactView.tsx` (full rewrite)
**Step 1: Rewrite PublicContactView**
Replace the entire content of `apps/portal/src/features/support/views/PublicContactView.tsx` with:
```tsx
"use client";
import { Mail, MapPin, Phone, MessageSquare, Clock, Send, ExternalLink } from "lucide-react";
import { ContactForm } from "@/features/support/components";
/**
* PublicContactView - Focused contact page with form + sidebar info
*/
export function PublicContactView() {
return (
<div className="max-w-6xl mx-auto px-4 pb-0">
{/* Header */}
<div className="text-center mb-12 pt-8">
<div className="inline-flex items-center justify-center w-16 h-16 bg-primary/10 rounded-2xl mb-4 text-primary">
<Send className="h-8 w-8" />
</div>
<h1 className="text-4xl sm:text-5xl font-extrabold text-foreground mb-4 tracking-tight">
Get in Touch
</h1>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto leading-relaxed">
Our English-speaking team is here to help. Fill out the form below or reach us through any
of the channels listed.
</p>
</div>
{/* Two-column layout: Form + Sidebar */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-10 mb-16">
{/* Contact Form - takes 2/3 width */}
<div className="lg:col-span-2">
<ContactForm />
</div>
{/* Sidebar - takes 1/3 width */}
<div className="space-y-6">
{/* Phone */}
<a
href="tel:0120-660-470"
className="group flex items-center gap-4 bg-white rounded-2xl border border-border/60 p-5 hover:border-primary/40 hover:shadow-md transition-all duration-200"
>
<div className="h-11 w-11 rounded-xl bg-primary/10 flex items-center justify-center text-primary group-hover:bg-primary group-hover:text-white transition-colors shrink-0">
<Phone className="h-5 w-5" />
</div>
<div>
<h3 className="font-bold text-foreground text-sm group-hover:text-primary transition-colors">
Call Us
</h3>
<p className="text-base font-bold text-primary">0120-660-470</p>
<p className="text-xs text-muted-foreground">Toll-free in Japan</p>
</div>
</a>
{/* Live Chat */}
<button
type="button"
onClick={() => {
/* Trigger chat */
}}
className="group flex items-center gap-4 bg-white rounded-2xl border border-border/60 p-5 hover:border-blue-500/40 hover:shadow-md transition-all duration-200 text-left w-full"
>
<div className="h-11 w-11 rounded-xl bg-blue-500/10 flex items-center justify-center text-blue-500 group-hover:bg-blue-500 group-hover:text-white transition-colors shrink-0">
<MessageSquare className="h-5 w-5" />
</div>
<div>
<h3 className="font-bold text-foreground text-sm group-hover:text-blue-500 transition-colors">
Live Chat
</h3>
<div className="flex items-center gap-2">
<span className="relative flex h-2 w-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-success opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-success"></span>
</span>
<span className="text-sm text-muted-foreground">Available now</span>
</div>
</div>
</button>
{/* Email */}
<a
href="mailto:support@assist-solutions.jp"
className="group flex items-center gap-4 bg-white rounded-2xl border border-border/60 p-5 hover:border-emerald-500/40 hover:shadow-md transition-all duration-200"
>
<div className="h-11 w-11 rounded-xl bg-emerald-500/10 flex items-center justify-center text-emerald-500 group-hover:bg-emerald-500 group-hover:text-white transition-colors shrink-0">
<Mail className="h-5 w-5" />
</div>
<div>
<h3 className="font-bold text-foreground text-sm group-hover:text-emerald-500 transition-colors">
Email Us
</h3>
<p className="text-sm text-muted-foreground">support@assist-solutions.jp</p>
</div>
</a>
{/* Business Hours */}
<div className="bg-muted/30 rounded-2xl p-5">
<div className="flex items-center gap-3 mb-3">
<Clock className="h-5 w-5 text-muted-foreground" />
<h3 className="font-bold text-foreground text-sm">Business Hours</h3>
</div>
<p className="text-sm text-muted-foreground">Mon - Fri, 9:30 AM - 6:00 PM JST</p>
</div>
{/* Office Location */}
<div className="bg-white rounded-2xl border border-border/60 p-5">
<div className="flex items-center gap-3 mb-3">
<MapPin className="h-5 w-5 text-primary" />
<h3 className="font-bold text-foreground text-sm">Our Office</h3>
</div>
<address className="text-sm text-muted-foreground leading-relaxed not-italic mb-3">
3F Azabu Maruka Bldg., 3-8-2 Higashi Azabu,
<br />
Minato-ku, Tokyo 106-0044
</address>
<p className="text-xs text-muted-foreground mb-3">
5 min walk from Exit 6, Azabu-Juban Station
</p>
<a
href="https://www.google.com/maps/dir//Assist+Solutions+Corp,+3-8-2+Higashi+Azabu,+Minato-ku,+Tokyo"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 text-primary text-sm font-medium hover:underline"
>
Get Directions
<ExternalLink className="h-3.5 w-3.5" />
</a>
</div>
{/* Small Map */}
<div className="rounded-2xl overflow-hidden border border-border/60 h-[200px]">
<iframe
title="Assist Solutions Corp Office"
src="https://www.google.com/maps?q=Assist+Solutions+Corp,+3-8-2+Higashi+Azabu,+Minato-ku,+Tokyo&output=embed"
className="w-full h-full"
loading="lazy"
allowFullScreen
referrerPolicy="no-referrer-when-downgrade"
/>
</div>
</div>
</div>
</div>
);
}
export default PublicContactView;
```
**Step 2: Verify no lint errors**
Run: `pnpm lint --filter @customer-portal/portal`
**Step 3: Commit**
```
feat: rewrite contact page with two-column layout using shared ContactForm
```
---
### Task 4: Rewrite Support Page as Self-Service Hub
**Files:**
- Modify: `apps/portal/src/features/support/views/PublicSupportView.tsx` (full rewrite)
**Step 1: Rewrite PublicSupportView**
Replace the entire content of `apps/portal/src/features/support/views/PublicSupportView.tsx` with:
```tsx
"use client";
import { useState } from "react";
import Image from "next/image";
import Link from "next/link";
import {
HelpCircle,
Wifi,
Smartphone,
Lock,
Building2,
CreditCard,
Wrench,
Download,
ChevronDown,
Send,
} from "lucide-react";
import { ContactForm } from "@/features/support/components";
import { supportDownloads } from "@/features/landing-page/data";
import { cn } from "@/shared/utils";
// =============================================================================
// DATA
// =============================================================================
const KNOWLEDGE_BASE_CATEGORIES = [
{
title: "Internet & Wi-Fi",
description: "Router setup, connection issues, speed troubleshooting",
icon: Wifi,
color: "text-blue-500",
bgColor: "bg-blue-500/10",
hoverBorder: "hover:border-blue-500/40",
},
{
title: "Phone & SIM",
description: "SIM activation, plan changes, number porting",
icon: Smartphone,
color: "text-green-500",
bgColor: "bg-green-500/10",
hoverBorder: "hover:border-green-500/40",
},
{
title: "VPN & Streaming",
description: "VPN router setup, streaming access, configuration",
icon: Lock,
color: "text-purple-500",
bgColor: "bg-purple-500/10",
hoverBorder: "hover:border-purple-500/40",
},
{
title: "Business Solutions",
description: "Office networks, dedicated lines, enterprise support",
icon: Building2,
color: "text-orange-500",
bgColor: "bg-orange-500/10",
hoverBorder: "hover:border-orange-500/40",
},
{
title: "Billing & Account",
description: "Invoices, payments, account changes, contracts",
icon: CreditCard,
color: "text-pink-500",
bgColor: "bg-pink-500/10",
hoverBorder: "hover:border-pink-500/40",
},
{
title: "General Tech Support",
description: "Device help, software issues, general troubleshooting",
icon: Wrench,
color: "text-amber-500",
bgColor: "bg-amber-500/10",
hoverBorder: "hover:border-amber-500/40",
},
];
const FAQ_ITEMS = [
{
question: "How do I set up my internet router?",
answer:
"After your installation appointment, connect the provided router to the NTT ONU device using the included LAN cable. Power on the router and connect to the Wi-Fi network using the credentials on the router label. If you need help, contact our support team or use our remote support tools.",
},
{
question: "How do I activate my SIM card?",
answer:
"Insert the SIM card into your unlocked phone. You should receive a confirmation email with APN settings. Go to Settings > Mobile Data > APN and enter the provided settings. Restart your phone and you should be connected within a few minutes.",
},
{
question: "What payment methods do you accept?",
answer:
"We accept major credit cards (Visa, Mastercard, American Express), bank transfers, and convenience store payments. Foreign credit cards are accepted for all our services.",
},
{
question: "How do I contact support outside business hours?",
answer:
"You can send us an email or submit a contact form at any time — we'll respond within 24 hours on the next business day. For urgent issues, our live chat may have extended availability.",
},
{
question: "Can I change my plan after signing up?",
answer:
"Yes, you can change your plan at any time. Contact our support team and we'll help you switch to a plan that better fits your needs. Changes typically take effect from the next billing cycle.",
},
];
// =============================================================================
// COMPONENT
// =============================================================================
/**
* PublicSupportView - Self-service support hub
*/
export function PublicSupportView() {
const [expandedFaq, setExpandedFaq] = useState<number | null>(null);
return (
<div className="max-w-6xl mx-auto px-4 pb-0">
{/* Header */}
<div className="text-center mb-12 pt-8">
<div className="inline-flex items-center justify-center w-16 h-16 bg-primary/10 rounded-2xl mb-4 text-primary">
<HelpCircle className="h-8 w-8" />
</div>
<h1 className="text-4xl sm:text-5xl font-extrabold text-foreground mb-4 tracking-tight">
How Can We Help?
</h1>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto leading-relaxed">
Find answers, download remote support tools, or send us a message. Our English-speaking
team is ready to assist.
</p>
</div>
{/* Knowledge Base Categories */}
<section className="mb-16">
<h2 className="text-2xl font-bold text-foreground mb-6 text-center">Browse by Topic</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{KNOWLEDGE_BASE_CATEGORIES.map(category => {
const Icon = category.icon;
return (
<div
key={category.title}
className={cn(
"bg-white rounded-2xl border border-border/60 p-6 transition-all duration-200 hover:shadow-md",
category.hoverBorder
)}
>
<div className="flex items-start gap-4">
<div
className={cn(
"h-11 w-11 rounded-xl flex items-center justify-center shrink-0",
category.bgColor,
category.color
)}
>
<Icon className="h-5 w-5" />
</div>
<div>
<h3 className="font-bold text-foreground text-sm mb-1">{category.title}</h3>
<p className="text-sm text-muted-foreground leading-relaxed">
{category.description}
</p>
</div>
</div>
</div>
);
})}
</div>
</section>
{/* Remote Support Tools */}
<section className="mb-16">
<h2 className="text-2xl font-bold text-foreground mb-2 text-center">
Remote Support Tools
</h2>
<p className="text-muted-foreground text-center mb-6">
Download one of these tools so our technicians can assist you remotely.
</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{supportDownloads.map(tool => (
<a
key={tool.title}
href={tool.href}
target="_blank"
rel="noopener noreferrer"
className="group bg-white rounded-2xl border border-border/60 p-6 hover:border-primary/40 hover:shadow-md transition-all duration-200"
>
<div className="flex items-start gap-5">
<div className="w-16 h-16 rounded-xl bg-muted/30 flex items-center justify-center shrink-0 overflow-hidden">
<Image
src={tool.image}
alt={tool.title}
width={48}
height={48}
className="object-contain"
/>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<h3 className="font-bold text-foreground group-hover:text-primary transition-colors">
{tool.title}
</h3>
<Download className="h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors" />
</div>
<p className="text-sm text-muted-foreground leading-relaxed mb-2">
{tool.description}
</p>
<p className="text-xs font-medium text-primary">{tool.useCase}</p>
</div>
</div>
</a>
))}
</div>
</section>
{/* FAQ */}
<section className="mb-16">
<h2 className="text-2xl font-bold text-foreground mb-6 text-center flex items-center justify-center gap-2">
Frequently Asked Questions
</h2>
<div className="max-w-3xl mx-auto space-y-3">
{FAQ_ITEMS.map((item, index) => {
const isExpanded = expandedFaq === index;
return (
<div
key={index}
className="bg-white rounded-xl border border-border/60 overflow-hidden"
>
<button
type="button"
onClick={() => setExpandedFaq(isExpanded ? null : index)}
className="w-full flex items-center justify-between p-4 text-left hover:bg-muted/30 transition-colors"
>
<span className="font-medium text-foreground text-sm pr-4">{item.question}</span>
<ChevronDown
className={cn(
"h-4 w-4 text-muted-foreground flex-shrink-0 transition-transform",
isExpanded && "rotate-180"
)}
/>
</button>
{isExpanded && (
<div className="px-4 pb-4">
<p className="text-sm text-muted-foreground leading-relaxed">{item.answer}</p>
</div>
)}
</div>
);
})}
</div>
</section>
{/* Contact Form Fallback */}
<section className="mb-12">
<div className="text-center mb-6">
<div className="inline-flex items-center justify-center w-12 h-12 bg-primary/10 rounded-xl mb-3 text-primary">
<Send className="h-6 w-6" />
</div>
<h2 className="text-2xl font-bold text-foreground">Still Need Help?</h2>
<p className="text-muted-foreground mt-1">
Send us a message and we&apos;ll get back to you within 24 hours.
</p>
</div>
<div className="max-w-2xl mx-auto">
<ContactForm />
</div>
</section>
{/* Existing Customer CTA */}
<div className="text-center pt-8 border-t border-border/60">
<p className="text-muted-foreground">
Already have an account?{" "}
<Link
href="/auth/login"
className="font-semibold text-primary hover:text-primary/80 hover:underline transition-colors"
>
Sign in
</Link>{" "}
to access your dashboard and support tickets.
</p>
</div>
</div>
);
}
export default PublicSupportView;
```
**Step 2: Verify no lint errors**
Run: `pnpm lint --filter @customer-portal/portal`
**Step 3: Commit**
```
feat: rewrite support page as self-service hub with knowledge base, remote tools, FAQ
```
---
### Task 5: Add Support Page Route
**Files:**
- Create: `apps/portal/src/app/(public)/(site)/support/page.tsx`
**Step 1: Create the support route page**
Create `apps/portal/src/app/(public)/(site)/support/page.tsx`:
```tsx
/**
* Public Support Page
*
* Self-service support hub with knowledge base categories,
* remote support tools, FAQ, and contact form fallback.
*/
import type { Metadata } from "next";
import { PublicSupportView } from "@/features/support/views/PublicSupportView";
export const metadata: Metadata = {
title: "Support - Self-Service Help Center | Assist Solutions",
description:
"Find answers to common questions, download remote support tools, or contact our English-speaking team. No Japanese required.",
keywords: [
"IT support Japan",
"English tech support Tokyo",
"remote support Japan",
"expat tech help",
"Assist Solutions support",
],
openGraph: {
title: "Support - Help Center | Assist Solutions",
description:
"Self-service support hub for expats in Japan. FAQ, remote support tools, and direct contact options.",
type: "website",
},
};
export default function SupportPage() {
return <PublicSupportView />;
}
```
**Step 2: Verify no lint errors**
Run: `pnpm lint --filter @customer-portal/portal`
**Step 3: Verify type checking passes**
Run: `pnpm type-check`
**Step 4: Commit**
```
feat: add /support route for public support page
```
---
### Task 6: Update Support Views Barrel Export
**Files:**
- Modify: `apps/portal/src/features/support/views/index.ts`
**Step 1: Add PublicSupportView and PublicContactView to the barrel export**
The current `views/index.ts` only exports authenticated views. Add the public views:
```ts
// Current content:
export * from "./NewSupportCaseView";
export * from "./SupportCasesView";
export * from "./SupportCaseDetailView";
export * from "./SupportHomeView";
// Add these:
export * from "./PublicContactView";
export * from "./PublicSupportView";
```
Note: The contact page currently imports directly from the view file (`@/features/support/views/PublicContactView`), not from the barrel. After adding to barrel, update the contact page import in `apps/portal/src/app/(public)/(site)/contact/page.tsx` to use the barrel:
```tsx
// OLD:
import { PublicContactView } from "@/features/support/views/PublicContactView";
// NEW:
import { PublicContactView } from "@/features/support/views";
```
And update the new support page import in `apps/portal/src/app/(public)/(site)/support/page.tsx`:
```tsx
// OLD:
import { PublicSupportView } from "@/features/support/views/PublicSupportView";
// NEW:
import { PublicSupportView } from "@/features/support/views";
```
**Step 2: Verify no lint errors**
Run: `pnpm lint --filter @customer-portal/portal`
**Step 3: Verify type checking passes**
Run: `pnpm type-check`
**Step 4: Commit**
```
refactor: add public views to support barrel exports
```
---
### Task 7: Final Verification
**Step 1: Run full lint check**
Run: `pnpm lint`
**Step 2: Run type check**
Run: `pnpm type-check`
**Step 3: Verify all pages render** (manual or dev server if permitted)
Check these routes work:
- `/` — homepage with new hero text
- `/contact` — two-column contact page
- `/support` — self-service support hub
- `/help` — still redirects to `/contact`