Enhance Support & Contact Page and Navigation

- Updated the Public Contact Page to include a combined FAQ section, improving user assistance and engagement.
- Renamed the Public Support Page to Public Help Page, redirecting to the updated contact page for better user flow.
- Modified SiteFooter and PublicShell components to reflect updated navigation links, enhancing clarity and accessibility.
- Improved styling and layout for better visual consistency across the portal.
This commit is contained in:
tema 2026-01-16 18:52:05 +09:00
parent 933a461372
commit ab3561ba5c
10 changed files with 734 additions and 277 deletions

View File

@ -0,0 +1,23 @@
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Blog & Updates - Assist Solutions",
description:
"Tips, guides, and news for expats and businesses in Japan. Learn about internet setup, SIM cards, VPN services, and life in Japan.",
keywords: [
"Japan expat blog",
"internet Japan guide",
"SIM card Japan",
"living in Japan tips",
"Assist Solutions news",
],
openGraph: {
title: "Blog & Updates - Assist Solutions",
description: "Tips, guides, and news for the international community in Japan.",
type: "website",
},
};
export default function BlogLayout({ children }: { children: React.ReactNode }) {
return children;
}

View File

@ -0,0 +1,247 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { cn } from "@/shared/utils";
// Sample blog data
const categories = [
{ id: "all", label: "Latest", count: 8 },
{ id: "guides", label: "Guides", count: 3 },
{ id: "tech", label: "Tech Tips", count: 2 },
{ id: "news", label: "Company News", count: 2 },
{ id: "lifestyle", label: "Expat Life", count: 1 },
];
const authors = {
daisuke: {
name: "Daisuke Nagakawa",
role: "CEO, Assist Solutions",
avatar: "/assets/images/avatar-placeholder.png",
},
support: {
name: "Support Team",
role: "Assist Solutions",
avatar: "/assets/images/avatar-placeholder.png",
},
tech: {
name: "Tech Team",
role: "Assist Solutions",
avatar: "/assets/images/avatar-placeholder.png",
},
};
const blogPosts = [
{
id: "1",
slug: "getting-internet-japan-guide",
category: "guides",
categoryLabel: "Guides",
title: "The Complete Guide to Getting Internet in Japan",
excerpt:
"Moving to Japan and need internet? This comprehensive guide covers everything from choosing between fiber and mobile internet to understanding NTT's installation process.",
image: "/assets/images/blog-placeholder-1.jpg",
author: authors.support,
date: "2025-01-10",
featured: true,
},
{
id: "2",
slug: "5g-coverage-tokyo-2025",
category: "tech",
categoryLabel: "Tech Tips",
title: "5G Coverage in Tokyo: What You Need to Know in 2025",
excerpt:
"With Docomo, au, and SoftBank expanding their 5G networks, we break down the current coverage in Tokyo and which areas have the best connectivity.",
image: "/assets/images/blog-placeholder-2.jpg",
author: authors.tech,
date: "2025-01-08",
featured: true,
},
{
id: "3",
slug: "choosing-sim-card-japan",
category: "guides",
categoryLabel: "Guides",
title: "Data-Only vs Voice SIM: Which One Do You Need?",
excerpt:
"Confused about SIM card options in Japan? We explain the differences between data-only and voice SIMs, and help you choose the right one for your needs.",
image: "/assets/images/blog-placeholder-3.jpg",
author: authors.support,
date: "2025-01-05",
},
{
id: "4",
slug: "work-from-home-internet-tips",
category: "tech",
categoryLabel: "Tech Tips",
title: "Optimizing Your Home Network for Remote Work",
excerpt:
"Working from home in Japan? Learn how to set up your router, optimize Wi-Fi coverage, and troubleshoot common connectivity issues.",
image: "/assets/images/blog-placeholder-4.jpg",
author: authors.tech,
date: "2025-01-03",
},
{
id: "5",
slug: "assist-solutions-2024-review",
category: "news",
categoryLabel: "Company News",
title: "2024 Year in Review: Serving Our Community",
excerpt:
"A look back at the past year - new services launched, customer milestones reached, and our continued commitment to the international community in Japan.",
image: "/assets/images/blog-placeholder-5.jpg",
author: authors.daisuke,
date: "2024-12-28",
},
{
id: "6",
slug: "vpn-streaming-guide",
category: "guides",
categoryLabel: "Guides",
title: "Using VPN to Access Streaming Services in Japan",
excerpt:
"Want to watch your favorite shows from back home? Here's how to set up and use a VPN to access international streaming platforms while living in Japan.",
image: "/assets/images/blog-placeholder-6.jpg",
author: authors.support,
date: "2024-12-20",
},
{
id: "7",
slug: "new-office-announcement",
category: "news",
categoryLabel: "Company News",
title: "Expanded Support Hours & New Team Members",
excerpt:
"We're excited to announce extended support hours and welcome new bilingual team members to better serve our growing customer base.",
image: "/assets/images/blog-placeholder-7.jpg",
author: authors.daisuke,
date: "2024-12-15",
},
{
id: "8",
slug: "moving-to-japan-checklist",
category: "lifestyle",
categoryLabel: "Expat Life",
title: "Moving to Japan: Your Essential IT & Utilities Checklist",
excerpt:
"From setting up a bank account to getting internet installed - everything you need to know about utilities and connectivity when relocating to Japan.",
image: "/assets/images/blog-placeholder-8.jpg",
author: authors.support,
date: "2024-12-10",
},
];
export default function BlogPage() {
const [activeCategory, setActiveCategory] = useState("all");
const filteredPosts =
activeCategory === "all"
? blogPosts
: blogPosts.filter(post => post.category === activeCategory);
return (
<div className="max-w-7xl mx-auto px-4 py-8 sm:py-12">
<div className="grid grid-cols-1 lg:grid-cols-[240px_1fr] gap-10 lg:gap-14">
{/* Sidebar */}
<aside className="lg:sticky lg:top-24 lg:self-start">
<div className="mb-8">
<h1 className="text-3xl sm:text-4xl font-extrabold text-foreground leading-tight">
Blog
</h1>
<p className="text-base italic text-primary mt-1">&amp; Updates</p>
<p className="text-sm text-muted-foreground mt-4 leading-relaxed">
Tips, guides, and news for the international community in Japan.
</p>
</div>
<div className="border-t border-border/60 pt-6">
<h2 className="text-sm font-bold text-foreground mb-4">Categories</h2>
<nav className="space-y-1">
{categories.map(category => (
<button
key={category.id}
type="button"
onClick={() => setActiveCategory(category.id)}
className={cn(
"w-full flex items-center justify-between px-3 py-2 rounded-lg text-sm font-medium transition-colors",
activeCategory === category.id
? "bg-primary/10 text-primary"
: "text-muted-foreground hover:text-foreground hover:bg-muted/50"
)}
>
<span>{category.label}</span>
<span
className={cn(
"text-xs px-2 py-0.5 rounded-full",
activeCategory === category.id
? "bg-primary/20 text-primary"
: "bg-muted text-muted-foreground"
)}
>
{category.count}
</span>
</button>
))}
</nav>
</div>
</aside>
{/* Blog Grid */}
<main>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{filteredPosts.map(post => (
<article key={post.id} className="group">
<Link href={`/blog/${post.slug}`} className="block">
{/* Image */}
<div className="relative aspect-[16/10] rounded-2xl overflow-hidden bg-muted mb-4">
<div className="absolute inset-0 bg-gradient-to-br from-primary/20 to-sky-200/30 flex items-center justify-center">
<span className="text-4xl font-bold text-primary/30">
{post.title.charAt(0)}
</span>
</div>
{/* Hover overlay */}
<div className="absolute inset-0 bg-primary/5 opacity-0 group-hover:opacity-100 transition-opacity" />
</div>
{/* Category */}
<span className="text-xs font-semibold text-primary uppercase tracking-wider">
{post.categoryLabel}
</span>
{/* Title */}
<h2 className="text-xl font-bold text-foreground mt-2 mb-3 group-hover:text-primary transition-colors leading-snug">
{post.title}
</h2>
{/* Excerpt */}
<p className="text-sm text-muted-foreground leading-relaxed line-clamp-3 mb-4">
{post.excerpt}
</p>
{/* Author */}
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-muted flex items-center justify-center text-muted-foreground text-sm font-bold">
{post.author.name.charAt(0)}
</div>
<div>
<p className="text-sm font-semibold text-foreground">{post.author.name}</p>
<p className="text-xs text-muted-foreground">{post.author.role}</p>
</div>
</div>
</Link>
</article>
))}
</div>
{/* Empty state */}
{filteredPosts.length === 0 && (
<div className="text-center py-16">
<p className="text-muted-foreground">No posts found in this category.</p>
</div>
)}
</main>
</div>
</div>
);
}

View File

@ -1,20 +1,27 @@
/**
* Public Contact Page
* Public Support & Contact Page
*
* Contact form for unauthenticated users.
* Combined FAQ, contact options, and contact form for unauthenticated users.
*/
import type { Metadata } from "next";
import { PublicContactView } from "@/features/support/views/PublicContactView";
export const metadata: Metadata = {
title: "Contact Us - Assist Solutions | Get Support",
title: "Support & Contact - Assist Solutions | Get Help",
description:
"Contact Assist Solutions for internet, mobile, and IT support inquiries. Call toll-free 0120-660-470 or reach us online. English and Japanese support available.",
keywords: ["contact Assist Solutions", "IT support Tokyo", "customer service"],
"Get support from Assist Solutions. FAQ, live chat, phone support (0120-660-470), and contact form. English and Japanese support available for internet, mobile, and IT services.",
keywords: [
"contact Assist Solutions",
"IT support Tokyo",
"customer service Japan",
"English support",
"FAQ",
],
openGraph: {
title: "Contact Assist Solutions",
description: "Get in touch with our bilingual support team. Toll-free: 0120-660-470",
title: "Support & Contact - Assist Solutions",
description:
"Get help with your internet, mobile, or IT services. Bilingual support available. Toll-free: 0120-660-470",
type: "website",
},
};

View File

@ -1,11 +1,11 @@
/**
* Public Support Page
* Public Help Page
*
* FAQ and help center for unauthenticated users.
* Redirects to the combined Support & Contact page.
*/
import { PublicSupportView } from "@/features/support/views/PublicSupportView";
import { redirect } from "next/navigation";
export default function PublicSupportPage() {
return <PublicSupportView />;
export default function PublicHelpPage() {
redirect("/contact");
}

View File

@ -94,15 +94,15 @@ export function SiteFooter() {
href="/contact"
className="text-muted-foreground hover:text-foreground transition-colors"
>
Contact
Support & Contact
</Link>
</li>
<li>
<Link
href="/help"
href="/blog"
className="text-muted-foreground hover:text-foreground transition-colors"
>
Support
Blog
</Link>
</li>
</ul>

View File

@ -175,12 +175,6 @@ export function PublicShell({ children }: PublicShellProps) {
>
About
</Link>
<Link
href="/contact"
className="inline-flex items-center px-3 py-2 rounded-md hover:text-foreground transition-colors"
>
Contact
</Link>
<Link
href="/blog"
className="hidden sm:inline-flex items-center px-3 py-2 rounded-md hover:text-foreground transition-colors"
@ -188,8 +182,8 @@ export function PublicShell({ children }: PublicShellProps) {
Blog
</Link>
<Link
href="/help"
className="hidden md:inline-flex items-center px-3 py-2 rounded-md hover:text-foreground transition-colors"
href="/contact"
className="inline-flex items-center px-3 py-2 rounded-md hover:text-foreground transition-colors"
>
Support
</Link>

View File

@ -208,24 +208,21 @@ export function SignupForm({
isSubmitting,
} = form;
const normalizeAutofillValue = useCallback(
(field: string, value: string) => {
switch (field) {
case "phoneCountryCode": {
let normalized = value.replace(/[^\d+]/g, "");
if (!normalized.startsWith("+")) normalized = "+" + normalized.replace(/\+/g, "");
return normalized.slice(0, 5);
}
case "phone":
return value.replace(/\D/g, "");
case "address.postcode":
return formatJapanesePostalCode(value);
default:
return value;
const normalizeAutofillValue = useCallback((field: string, value: string) => {
switch (field) {
case "phoneCountryCode": {
let normalized = value.replace(/[^\d+]/g, "");
if (!normalized.startsWith("+")) normalized = "+" + normalized.replace(/\+/g, "");
return normalized.slice(0, 5);
}
},
[formatJapanesePostalCode]
);
case "phone":
return value.replace(/\D/g, "");
case "address.postcode":
return formatJapanesePostalCode(value);
default:
return value;
}
}, []);
const syncStepValues = useCallback(
(shouldFlush = true) => {

View File

@ -99,7 +99,6 @@ export function PublicLandingView() {
"Utilizing NTT's optical fiber network, we deliver one of the most reliable Internet connections in Japan.",
icon: <Wifi className="h-8 w-8 text-primary" />,
href: "/services/internet",
price: "From 5,280 JPY/mo",
},
{
title: "Phone Plans",
@ -107,7 +106,6 @@ export function PublicLandingView() {
"Using NTT DOCOMO's vast mobile network, we deliver one of the most cost-friendly SIM card services in Japan.",
icon: <Smartphone className="h-8 w-8 text-primary" />,
href: "/services/sim",
price: "From 1,078 JPY/mo",
},
{
title: "Business Solutions",
@ -115,7 +113,6 @@ export function PublicLandingView() {
"Dedicated Internet Access, office network setup, data center services, and ongoing tech support.",
icon: <Building2 className="h-8 w-8 text-primary" />,
href: "/services/business",
price: "Custom Quote",
},
{
title: "VPN",
@ -123,14 +120,12 @@ export function PublicLandingView() {
"Choose any of our VPN server locations that connect directly from Tokyo with static routing.",
icon: <Lock className="h-8 w-8 text-primary" />,
href: "/services/vpn",
price: "From 1,100 JPY/mo",
},
{
title: "TV Services",
description: "A variety of options for customers such as Satellite TV and Optical Fiber TV.",
icon: <Tv className="h-8 w-8 text-primary" />,
href: "/services/tv",
price: "From 4,389 JPY/mo",
},
{
title: "Onsite Support",
@ -138,7 +133,6 @@ export function PublicLandingView() {
"Professional technical support at your residence or office for setup, configuration, and troubleshooting.",
icon: <Wrench className="h-8 w-8 text-primary" />,
href: "/services/onsite",
price: "From 5,000 JPY",
},
];
const isScrollingRef = useRef(false);
@ -251,8 +245,20 @@ export function PublicLandingView() {
return (
<div className="space-y-0 pb-8 pt-0">
{/* Hero Section */}
<section className="relative left-1/2 right-1/2 w-screen -translate-x-1/2 bg-[#f7f7f7] py-12 sm:py-16">
<div className="mx-auto max-w-6xl grid grid-cols-1 lg:grid-cols-[1.05fr_minmax(0,0.95fr)] items-center gap-10 lg:gap-16 px-6 sm:px-10 lg:px-14">
<section className="relative left-1/2 right-1/2 w-screen -translate-x-1/2 bg-gradient-to-br from-slate-50 via-white to-sky-50/50 py-12 sm:py-16 overflow-hidden">
{/* Abstract gradient blobs */}
<div className="absolute inset-0 overflow-hidden pointer-events-none">
{/* Top-left blob */}
<div className="absolute -top-24 -left-24 w-96 h-96 bg-gradient-to-br from-primary/20 to-sky-300/20 rounded-full blur-3xl opacity-60" />
{/* Top-right blob */}
<div className="absolute -top-12 right-0 w-80 h-80 bg-gradient-to-bl from-cyan-200/30 to-blue-300/20 rounded-full blur-3xl opacity-50" />
{/* Bottom-left blob */}
<div className="absolute bottom-0 left-1/4 w-72 h-72 bg-gradient-to-tr from-sky-200/25 to-indigo-200/20 rounded-full blur-3xl opacity-40" />
{/* Center-right accent */}
<div className="absolute top-1/2 right-1/4 w-64 h-64 bg-gradient-to-tl from-primary/10 to-cyan-300/15 rounded-full blur-3xl opacity-50" />
</div>
<div className="relative mx-auto max-w-6xl grid grid-cols-1 lg:grid-cols-[1.05fr_minmax(0,0.95fr)] items-center gap-10 lg:gap-16 px-6 sm:px-10 lg:px-14">
<div className="space-y-6 text-left max-w-2xl">
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold leading-tight text-foreground">
<span className="block whitespace-nowrap">One Stop Solution</span>
@ -400,7 +406,6 @@ export function PublicLandingView() {
<p className="text-muted-foreground leading-relaxed flex-grow">
{service.description}
</p>
<p className="text-sm font-bold text-primary mt-4">{service.price}</p>
</article>
</Link>
))}

View File

@ -119,34 +119,51 @@ export function AboutUsView() {
}, [computeScrollAmount]);
return (
<div className="max-w-6xl mx-auto space-y-12 px-6 sm:px-8">
{/* Hero */}
<section className="grid grid-cols-1 lg:grid-cols-[1.05fr_minmax(0,0.95fr)] gap-10 items-center">
<div className="space-y-5">
<h1 className="text-3xl sm:text-4xl font-bold text-primary leading-tight">About Us</h1>
<div className="space-y-4 text-muted-foreground leading-relaxed">
<p>
Assist Solutions Corp. is a privately-owned entrepreneurial IT service company. We
specialize in serving Japan&apos;s international community with the most reliable and
cost-efficient IT & TV solutions available.
</p>
<p>
We are dedicated to providing comfortable support for our customer&apos;s diverse
needs in both English and Japanese. Our excellent bi-lingual support, flexible
service, and deep industry knowledge set us apart.
</p>
<div className="space-y-12">
{/* Hero with geometric pattern */}
<section className="relative left-1/2 right-1/2 w-screen -translate-x-1/2 bg-gradient-to-br from-slate-50 to-sky-50/30 py-12 sm:py-16 overflow-hidden">
{/* Dot grid pattern */}
<div
className="absolute inset-0 opacity-[0.4]"
style={{
backgroundImage: `radial-gradient(circle, #0ea5e9 1px, transparent 1px)`,
backgroundSize: "24px 24px",
}}
/>
{/* Subtle gradient overlay for depth */}
<div className="absolute inset-0 bg-gradient-to-t from-white/80 via-transparent to-white/40" />
<div className="relative max-w-6xl mx-auto px-6 sm:px-8">
<div className="grid grid-cols-1 lg:grid-cols-[1.05fr_minmax(0,0.95fr)] gap-10 items-center">
<div className="space-y-5">
<h1 className="text-4xl sm:text-5xl font-extrabold text-primary leading-tight tracking-tight">
About Us
</h1>
<div className="space-y-4 text-muted-foreground leading-relaxed text-base sm:text-lg">
<p>
Assist Solutions Corp. is a privately-owned entrepreneurial IT service company. We
specialize in serving Japan&apos;s international community with the most reliable
and cost-efficient IT & TV solutions available.
</p>
<p>
We are dedicated to providing comfortable support for our customer&apos;s diverse
needs in both English and Japanese. Our excellent bi-lingual support, flexible
service, and deep industry knowledge set us apart.
</p>
</div>
</div>
<div className="relative h-full min-h-[420px]">
<Image
src="/assets/images/About us.png"
alt="Assist Solutions team in Tokyo"
fill
priority
className="object-contain drop-shadow-lg"
sizes="(max-width: 1024px) 100vw, 45vw"
/>
</div>
</div>
</div>
<div className="relative h-full min-h-[420px]">
<Image
src="/assets/images/About us.png"
alt="Assist Solutions team in Tokyo"
fill
priority
className="object-contain"
sizes="(max-width: 1024px) 100vw, 45vw"
/>
</div>
</section>
{/* Business Solutions Carousel */}

View File

@ -6,26 +6,81 @@ 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 { Mail, CheckCircle, MapPin } from "lucide-react";
import {
Mail,
CheckCircle,
MapPin,
Phone,
MessageSquare,
ChevronDown,
Clock,
HelpCircle,
Send,
ExternalLink,
} from "lucide-react";
import {
publicContactRequestSchema,
type PublicContactRequest,
} from "@customer-portal/domain/support";
import { apiClient, ApiError, isApiError } from "@/core/api";
import { cn } from "@/shared/utils";
const FAQ_ITEMS = [
{
question: "How do I get started with your services?",
answer:
"Simply browse our services, select a plan that fits your needs, and complete the checkout process. You can create an account during checkout or contact us for personalized assistance.",
},
{
question: "What payment methods do you accept?",
answer:
"We accept major credit cards (Visa, Mastercard, American Express) including foreign-issued cards, and bank transfers. Payment methods can be managed in your account settings.",
},
{
question: "How long does installation take?",
answer:
"Internet installation typically takes 2-4 weeks depending on your location and the type of installation required. SIM cards are shipped within 3-5 business days.",
},
{
question: "Can I change my plan after signing up?",
answer:
"Yes, you can upgrade or downgrade your plan at any time. Changes typically take effect at the start of your next billing cycle.",
},
{
question: "What is your cancellation policy?",
answer:
"Most services have a minimum contract period (typically 4 months for SIM, varies for internet). After this period, you can cancel with one month's notice.",
},
{
question: "Do you offer business solutions?",
answer:
"Yes, we offer dedicated business plans including Dedicated Internet Access, office LAN setup, data center services, and ongoing tech support. Please contact us for a custom quote.",
},
{
question: "Do you provide English support?",
answer:
"Yes! Our entire team is bilingual and provides full support in both English and Japanese. This includes phone support, email, and onsite visits.",
},
{
question: "What areas do you service?",
answer:
"We primarily serve the greater Tokyo area for onsite support. Internet and SIM services are available nationwide through NTT and Docomo networks.",
},
];
/**
* PublicContactView - Contact page with form, phone, chat, and location info
* PublicContactView - Combined Support & Contact page
*/
export function PublicContactView() {
const [isSubmitted, setIsSubmitted] = useState(false);
const [submitError, setSubmitError] = useState<string | null>(null);
const [expandedFaq, setExpandedFaq] = useState<number | 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)) {
@ -54,17 +109,17 @@ export function PublicContactView() {
if (isSubmitted) {
return (
<div className="max-w-lg mx-auto text-center py-12">
<div className="w-16 h-16 bg-success/10 rounded-full flex items-center justify-center mx-auto mb-6">
<CheckCircle className="h-8 w-8 text-success" />
<div className="max-w-lg mx-auto text-center py-16">
<div className="w-20 h-20 bg-success/10 rounded-full flex items-center justify-center mx-auto mb-6">
<CheckCircle className="h-10 w-10 text-success" />
</div>
<h1 className="text-2xl font-bold text-foreground mb-2">Message Sent!</h1>
<p className="text-muted-foreground mb-6">
<h1 className="text-3xl font-bold text-foreground mb-3">Message Sent!</h1>
<p className="text-muted-foreground mb-8 text-lg">
Thank you for contacting us. We&apos;ll get back to you within 24 hours.
</p>
<div className="flex gap-4 justify-center">
<Button as="a" href="/help" variant="outline">
Back to Support
<Button as="a" href="/" variant="outline">
Back to Home
</Button>
<Button as="a" href="/services">
Browse Services
@ -75,224 +130,336 @@ export function PublicContactView() {
}
return (
<div className="max-w-6xl mx-auto px-4">
<div className="max-w-6xl mx-auto px-4 pb-16">
{/* Header */}
<div className="text-center mb-16 pt-8">
<h1 className="text-4xl sm:text-5xl font-extrabold text-foreground mb-6 tracking-tight">
Get in Touch
<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">
Support & Contact
</h1>
<p className="text-xl text-muted-foreground max-w-2xl mx-auto leading-relaxed">
Have a question about our services? We're here to help you find the perfect solution for
your stay in Japan.
<p className="text-lg text-muted-foreground max-w-2xl mx-auto leading-relaxed">
Have a question? We&apos;re here to help. Reach out through any channel below.
</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-12 gap-10">
{/* Left Column - Contact Form */}
<div className="lg:col-span-7">
<div className="bg-card rounded-2xl border border-border/60 shadow-sm overflow-hidden">
<div className="p-6 sm:p-8 border-b border-border/60 bg-muted/20">
<div className="flex items-center gap-3">
<div className="h-10 w-10 rounded-xl bg-primary/10 flex items-center justify-center text-primary">
<Mail className="h-5 w-5" />
</div>
<div>
<h2 className="text-xl font-bold text-foreground">Send a Message</h2>
<p className="text-sm text-muted-foreground">
We typically reply within 24 hours
</p>
</div>
</div>
{/* Quick Contact Options */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-12">
{/* Phone */}
<a
href="tel:0120-660-470"
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-center gap-4">
<div className="h-12 w-12 rounded-xl bg-primary/10 flex items-center justify-center text-primary group-hover:bg-primary group-hover:text-white transition-colors">
<Phone className="h-6 w-6" />
</div>
<div className="p-6 sm:p-8">
{submitError && (
<AlertBanner variant="error" title="Error" className="mb-6">
{submitError}
</AlertBanner>
)}
<form onSubmit={event => void form.handleSubmit(event)} className="space-y-6">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
<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-background"
/>
</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-background"
/>
</FormField>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
<FormField
label="Phone (Optional)"
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-background"
/>
</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-background"
/>
</FormField>
</div>
<FormField
label="Message"
error={form.touched.message ? form.errors.message : undefined}
required
>
<textarea
className="flex min-h-[160px] w-full rounded-lg border border-input bg-background 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"
value={form.values.message}
onChange={e => form.setValue("message", e.target.value)}
onBlur={() => form.setTouchedField("message")}
placeholder="Tell us more about your inquiry..."
rows={5}
/>
</FormField>
<Button
type="submit"
className="w-full sm:w-auto min-w-[160px]"
size="lg"
disabled={form.isSubmitting}
isLoading={form.isSubmitting}
loadingText="Sending..."
>
Send Message
</Button>
</form>
<p className="text-xs text-muted-foreground mt-6 pt-6 border-t border-border/60">
By submitting this form, you agree to our{" "}
<Link href="#" className="text-primary hover:underline font-medium">
Privacy Policy
</Link>
. Your information is secure and will only be used to respond to your inquiry.
</p>
<div>
<h3 className="font-bold text-foreground group-hover:text-primary transition-colors">
Call Us
</h3>
<p className="text-lg font-bold text-primary">0120-660-470</p>
<p className="text-xs text-muted-foreground">Toll-free in Japan</p>
</div>
</div>
</div>
</a>
{/* Right Column - Contact Info */}
<div className="lg:col-span-5 space-y-6">
{/* By Phone */}
<div className="bg-card rounded-2xl border border-border/60 p-6 sm:p-8 hover:border-primary/30 transition-colors duration-300">
<h2 className="text-xl font-bold text-foreground mb-4">Phone Support</h2>
<div className="space-y-4">
<div>
<div className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-1">
Japan (Toll Free)
</div>
<a
href="tel:0120-660-470"
className="text-2xl font-bold text-foreground hover:text-primary transition-colors inline-block"
>
0120-660-470
</a>
</div>
<div>
<div className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-1">
International
</div>
<a
href="tel:+81-3-3560-1006"
className="text-lg font-semibold text-foreground hover:text-primary transition-colors inline-block"
>
+81-3-3560-1006
</a>
</div>
<div className="text-sm text-muted-foreground pt-4 border-t border-border/60">
9:30 - 18:00 JST (Mon - Fri)
</div>
{/* Chat */}
<button
type="button"
onClick={() => {
/* Trigger chat */
}}
className="group bg-white rounded-2xl border border-border/60 p-6 hover:border-blue-500/40 hover:shadow-md transition-all duration-200 text-left"
>
<div className="flex items-center gap-4">
<div className="h-12 w-12 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">
<MessageSquare className="h-6 w-6" />
</div>
</div>
{/* By Chat */}
<div className="bg-card rounded-2xl border border-border/60 p-6 sm:p-8 hover:border-blue-500/30 transition-colors duration-300">
<h2 className="text-xl font-bold text-foreground mb-4">Live Chat</h2>
<p className="text-muted-foreground mb-6">
Need quick answers? Chat with our support team directly in your browser.
</p>
<Button
variant="outline"
className="w-full justify-start"
onClick={() => {
/* Trigger chat logic would go here */
}}
>
<span className="flex items-center gap-2">
<span className="relative flex h-2 w-2 mr-1">
<div>
<h3 className="font-bold text-foreground 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>
Chat Available
</span>
</Button>
<span className="text-sm text-muted-foreground">Available now</span>
</div>
</div>
</div>
</button>
{/* Email */}
<a
href="mailto:support@assist-solutions.jp"
className="group bg-white rounded-2xl border border-border/60 p-6 hover:border-emerald-500/40 hover:shadow-md transition-all duration-200"
>
<div className="flex items-center gap-4">
<div className="h-12 w-12 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">
<Mail className="h-6 w-6" />
</div>
<div>
<h3 className="font-bold text-foreground group-hover:text-emerald-500 transition-colors">
Email Us
</h3>
<p className="text-sm text-muted-foreground">support@assist-solutions.jp</p>
</div>
</div>
</a>
</div>
{/* Business Hours Banner */}
<div className="bg-muted/30 rounded-xl p-4 mb-12 flex items-center justify-center gap-3 text-sm">
<Clock className="h-4 w-4 text-muted-foreground" />
<span className="text-muted-foreground">
<span className="font-medium text-foreground">Business Hours:</span> Mon - Fri, 9:30 AM -
6:00 PM JST
</span>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10 mb-16">
{/* FAQ Section */}
<div>
<h2 className="text-2xl font-bold text-foreground mb-6 flex items-center gap-2">
<HelpCircle className="h-6 w-6 text-primary" />
Frequently Asked Questions
</h2>
<div className="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>
</div>
{/* Contact Form */}
<div>
<h2 className="text-2xl font-bold text-foreground mb-6 flex items-center gap-2">
<Send className="h-6 w-6 text-primary" />
Send a Message
</h2>
<div className="bg-white rounded-2xl border border-border/60 p-6">
{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>
</div>
</div>
{/* Office Location */}
<div className="bg-white rounded-2xl border border-border/60 overflow-hidden">
<div className="grid grid-cols-1 lg:grid-cols-2">
{/* Map */}
<div className="h-[300px] lg:h-auto">
<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>
{/* Access / Location */}
<div className="bg-card rounded-2xl border border-border/60 p-6 sm:p-8 hover:border-primary/30 transition-colors duration-300">
{/* Address Info */}
<div className="p-8">
<div className="flex items-center gap-3 mb-6">
<div className="h-10 w-10 rounded-xl bg-primary/10 flex items-center justify-center text-primary">
<MapPin className="h-5 w-5" />
<div className="h-12 w-12 rounded-xl bg-primary/10 flex items-center justify-center text-primary">
<MapPin className="h-6 w-6" />
</div>
<h2 className="text-xl font-bold text-foreground">Visit Us</h2>
</div>
<div className="space-y-4">
<address className="text-muted-foreground leading-relaxed not-italic">
3F Azabu Maruka Bldg., 3-8-2 Higashi Azabu,
<br />
Minato-ku, Tokyo 106-0044
</address>
<div className="pt-4 border-t border-border/60">
<div>
<h2 className="text-xl font-bold text-foreground">Visit Our Office</h2>
<p className="text-sm text-muted-foreground">
Short walk from Exit 6 of Azabu-Juban Station
Walk-ins welcome during business hours
</p>
</div>
</div>
<div className="space-y-6">
<div>
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wider mb-2">
Address
</h3>
<address className="text-foreground leading-relaxed not-italic">
3F Azabu Maruka Bldg., 3-8-2 Higashi Azabu,
<br />
Minato-ku, Tokyo 106-0044
</address>
</div>
<div>
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wider mb-2">
Contact
</h3>
<p className="text-foreground">
Tel: 03-3560-1006
<br />
Fax: 03-3560-1007
</p>
</div>
<div>
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wider mb-2">
Access
</h3>
<p className="text-sm text-muted-foreground leading-relaxed">
5 min walk from Exit 6 of Azabu-Juban Station
<br />
(Subway Oedo Line / Nanboku Line)
</p>
</div>
<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-2 text-primary font-medium hover:underline"
>
Get Directions
<ExternalLink className="h-4 w-4" />
</a>
</div>
</div>
</div>
</div>
{/* Existing Customer CTA */}
<div className="text-center mt-12 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>
);
}