diff --git a/apps/portal/ARCHITECTURE.md b/apps/portal/ARCHITECTURE.md
index 4b725daf..c2d3f421 100644
--- a/apps/portal/ARCHITECTURE.md
+++ b/apps/portal/ARCHITECTURE.md
@@ -6,26 +6,31 @@ This document outlines the new feature-driven architecture implemented for the c
```
apps/portal/src/
-├── app/ # Next.js App Router pages
-├── components/ # Shared UI components (Design System)
-│ ├── ui/ # Base UI components (atoms)
-│ ├── layout/ # Layout components (organisms)
-│ └── common/ # Shared business components (molecules)
-├── features/ # Feature-specific modules
-│ ├── auth/ # Authentication feature
-│ ├── dashboard/ # Dashboard feature
-│ ├── billing/ # Billing feature
-│ ├── subscriptions/ # Subscriptions feature
-│ ├── catalog/ # Product catalog feature
-│ └── support/ # Support feature
-├── lib/ # Core utilities and services (replaces core/shared)
-│ ├── api/ # API client and base services
-│ ├── query.ts # Query client and keys
-│ ├── env.ts # Runtime env parsing
-│ ├── types/ # Shared TypeScript types
-│ └── utils/ # Utility functions (cn, currency, error-display, ...)
-├── providers/ # React context providers (e.g., QueryProvider)
-└── styles/ # Global styles and design tokens
+├── app/ # Next.js App Router entry points (route groups only)
+│ ├── (public)/ # Marketing + auth routes, pages import feature views
+│ ├── (authenticated)/ # Signed-in portal routes, thin wrappers around features
+│ ├── api/ # App Router API routes
+│ ├── favicon.ico / globals.css # Global assets
+│ └── layout.tsx # Root layout/providers
+├── components/ # Shared UI components (design system atoms/molecules)
+│ ├── ui/
+│ ├── layout/
+│ └── common/
+├── core/ # App-wide configuration (env, logger, providers)
+├── features/ # Feature-specific modules composed by routes
+│ ├── account/
+│ ├── auth/
+│ ├── billing/
+│ ├── catalog/
+│ ├── dashboard/
+│ ├── marketing/
+│ ├── orders/
+│ ├── service-management/
+│ ├── subscriptions/
+│ └── support/
+├── shared/ # Cross-feature helpers (e.g., constants, locale data)
+├── styles/ # Global styles and design tokens
+└── types/ # Portal-specific TypeScript types
```
## Design Principles
@@ -48,7 +53,7 @@ Each feature module contains:
- `utils/`: Utility functions
### 4. Centralized Shared Resources
-Common utilities, types, and components are centralized in the `lib/` and `components/` directories.
+Common utilities, types, and components are centralized in the `core/`, `shared/`, and `components/` directories.
## Feature Module Structure
@@ -118,20 +123,21 @@ import { DataTable } from '@/components/common';
import type { User, ApiResponse } from '@/types';
// Utility imports
-import { designSystem } from '@/lib/design-system';
-// Prefer feature services/hooks over direct apiClient usage in pages
-import { apiClient } from '@/lib/api/client';
+import { QueryProvider } from '@/core/providers';
+// Prefer feature services/hooks over direct api usage in pages
+import { logger } from '@/core/config';
```
### Path Mappings
- `@/*` - Root src directory
- `@/components/*` - Component library
+- `@/core/*` - App-wide configuration and providers
- `@/features/*` - Feature modules
-- `@/lib/*` - Core utilities
-- `@/types` - Type definitions
+- `@/shared/*` - Shared helpers/constants
- `@/styles/*` - Style files
-- `@shared/*` - Shared package
+- `@/types/*` - Portal-specific types
+- `@shared/*` - Shared package exports
## Migration Strategy
@@ -179,6 +185,14 @@ The migration to this new architecture will be done incrementally:
This ensures pages remain declarative and the feature layer encapsulates logic.
+### Route Layering
+
+- `(public)`: marketing landing and authentication flows. These routes render feature views such as `marketing/PublicLandingView` and `auth` screens while remaining server components by default.
+- `(authenticated)`: signed-in portal experience. Pages import dashboard, billing, subscriptions, etc. from the feature layer and rely on the shared route-group layout to provide navigation.
+- `api/`: App Router API endpoints remain colocated under `src/app/api` and can reuse feature services for data access.
+
+Only `layout.tsx`, `page.tsx`, and `loading.tsx` files live inside the route groups. All reusable UI, hooks, and services live under `src/features/**` to keep routing concerns thin.
+
### Current Feature Hooks/Services
- Catalog
@@ -191,3 +205,7 @@ This ensures pages remain declarative and the feature layer encapsulates logic.
- Service: `ordersService` (list/detail/create)
- Account
- Service: `accountService` (`/me/address`)
+- Support
+ - Views: `SupportCasesView`, `NewSupportCaseView` (mock data, ready for API wiring)
+- Marketing
+ - Views: `PublicLandingView`, `PublicLandingLoadingView`
diff --git a/apps/portal/src/app/(portal)/account/loading.tsx b/apps/portal/src/app/(authenticated)/account/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/account/loading.tsx
rename to apps/portal/src/app/(authenticated)/account/loading.tsx
diff --git a/apps/portal/src/app/(portal)/account/page.tsx b/apps/portal/src/app/(authenticated)/account/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/account/page.tsx
rename to apps/portal/src/app/(authenticated)/account/page.tsx
diff --git a/apps/portal/src/app/(portal)/account/profile/page.tsx b/apps/portal/src/app/(authenticated)/account/profile/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/account/profile/page.tsx
rename to apps/portal/src/app/(authenticated)/account/profile/page.tsx
diff --git a/apps/portal/src/app/(portal)/billing/invoices/[id]/loading.tsx b/apps/portal/src/app/(authenticated)/billing/invoices/[id]/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/billing/invoices/[id]/loading.tsx
rename to apps/portal/src/app/(authenticated)/billing/invoices/[id]/loading.tsx
diff --git a/apps/portal/src/app/(portal)/billing/invoices/[id]/page.tsx b/apps/portal/src/app/(authenticated)/billing/invoices/[id]/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/billing/invoices/[id]/page.tsx
rename to apps/portal/src/app/(authenticated)/billing/invoices/[id]/page.tsx
diff --git a/apps/portal/src/app/(portal)/billing/invoices/loading.tsx b/apps/portal/src/app/(authenticated)/billing/invoices/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/billing/invoices/loading.tsx
rename to apps/portal/src/app/(authenticated)/billing/invoices/loading.tsx
diff --git a/apps/portal/src/app/(portal)/billing/invoices/page.tsx b/apps/portal/src/app/(authenticated)/billing/invoices/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/billing/invoices/page.tsx
rename to apps/portal/src/app/(authenticated)/billing/invoices/page.tsx
diff --git a/apps/portal/src/app/(portal)/billing/payments/loading.tsx b/apps/portal/src/app/(authenticated)/billing/payments/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/billing/payments/loading.tsx
rename to apps/portal/src/app/(authenticated)/billing/payments/loading.tsx
diff --git a/apps/portal/src/app/(portal)/billing/payments/page.tsx b/apps/portal/src/app/(authenticated)/billing/payments/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/billing/payments/page.tsx
rename to apps/portal/src/app/(authenticated)/billing/payments/page.tsx
diff --git a/apps/portal/src/app/(portal)/catalog/internet/configure/page.tsx b/apps/portal/src/app/(authenticated)/catalog/internet/configure/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/catalog/internet/configure/page.tsx
rename to apps/portal/src/app/(authenticated)/catalog/internet/configure/page.tsx
diff --git a/apps/portal/src/app/(portal)/catalog/internet/page.tsx b/apps/portal/src/app/(authenticated)/catalog/internet/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/catalog/internet/page.tsx
rename to apps/portal/src/app/(authenticated)/catalog/internet/page.tsx
diff --git a/apps/portal/src/app/(portal)/catalog/loading.tsx b/apps/portal/src/app/(authenticated)/catalog/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/catalog/loading.tsx
rename to apps/portal/src/app/(authenticated)/catalog/loading.tsx
diff --git a/apps/portal/src/app/(portal)/catalog/page.tsx b/apps/portal/src/app/(authenticated)/catalog/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/catalog/page.tsx
rename to apps/portal/src/app/(authenticated)/catalog/page.tsx
diff --git a/apps/portal/src/app/(portal)/catalog/sim/configure/page.tsx b/apps/portal/src/app/(authenticated)/catalog/sim/configure/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/catalog/sim/configure/page.tsx
rename to apps/portal/src/app/(authenticated)/catalog/sim/configure/page.tsx
diff --git a/apps/portal/src/app/(portal)/catalog/sim/page.tsx b/apps/portal/src/app/(authenticated)/catalog/sim/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/catalog/sim/page.tsx
rename to apps/portal/src/app/(authenticated)/catalog/sim/page.tsx
diff --git a/apps/portal/src/app/(portal)/catalog/vpn/page.tsx b/apps/portal/src/app/(authenticated)/catalog/vpn/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/catalog/vpn/page.tsx
rename to apps/portal/src/app/(authenticated)/catalog/vpn/page.tsx
diff --git a/apps/portal/src/app/(portal)/checkout/loading.tsx b/apps/portal/src/app/(authenticated)/checkout/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/checkout/loading.tsx
rename to apps/portal/src/app/(authenticated)/checkout/loading.tsx
diff --git a/apps/portal/src/app/(portal)/checkout/page.tsx b/apps/portal/src/app/(authenticated)/checkout/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/checkout/page.tsx
rename to apps/portal/src/app/(authenticated)/checkout/page.tsx
diff --git a/apps/portal/src/app/(portal)/dashboard/loading.tsx b/apps/portal/src/app/(authenticated)/dashboard/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/dashboard/loading.tsx
rename to apps/portal/src/app/(authenticated)/dashboard/loading.tsx
diff --git a/apps/portal/src/app/(authenticated)/dashboard/page.tsx b/apps/portal/src/app/(authenticated)/dashboard/page.tsx
new file mode 100644
index 00000000..0871d6d4
--- /dev/null
+++ b/apps/portal/src/app/(authenticated)/dashboard/page.tsx
@@ -0,0 +1,5 @@
+import { DashboardView } from "@/features/dashboard";
+
+export default function DashboardPage() {
+ return ;
+}
diff --git a/apps/portal/src/app/(portal)/layout.tsx b/apps/portal/src/app/(authenticated)/layout.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/layout.tsx
rename to apps/portal/src/app/(authenticated)/layout.tsx
diff --git a/apps/portal/src/app/(portal)/orders/[id]/loading.tsx b/apps/portal/src/app/(authenticated)/orders/[id]/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/orders/[id]/loading.tsx
rename to apps/portal/src/app/(authenticated)/orders/[id]/loading.tsx
diff --git a/apps/portal/src/app/(portal)/orders/[id]/page.tsx b/apps/portal/src/app/(authenticated)/orders/[id]/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/orders/[id]/page.tsx
rename to apps/portal/src/app/(authenticated)/orders/[id]/page.tsx
diff --git a/apps/portal/src/app/(portal)/orders/loading.tsx b/apps/portal/src/app/(authenticated)/orders/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/orders/loading.tsx
rename to apps/portal/src/app/(authenticated)/orders/loading.tsx
diff --git a/apps/portal/src/app/(portal)/orders/page.tsx b/apps/portal/src/app/(authenticated)/orders/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/orders/page.tsx
rename to apps/portal/src/app/(authenticated)/orders/page.tsx
diff --git a/apps/portal/src/app/(portal)/subscriptions/[id]/loading.tsx b/apps/portal/src/app/(authenticated)/subscriptions/[id]/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/subscriptions/[id]/loading.tsx
rename to apps/portal/src/app/(authenticated)/subscriptions/[id]/loading.tsx
diff --git a/apps/portal/src/app/(portal)/subscriptions/[id]/page.tsx b/apps/portal/src/app/(authenticated)/subscriptions/[id]/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/subscriptions/[id]/page.tsx
rename to apps/portal/src/app/(authenticated)/subscriptions/[id]/page.tsx
diff --git a/apps/portal/src/app/(portal)/subscriptions/[id]/sim/cancel/page.tsx b/apps/portal/src/app/(authenticated)/subscriptions/[id]/sim/cancel/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/subscriptions/[id]/sim/cancel/page.tsx
rename to apps/portal/src/app/(authenticated)/subscriptions/[id]/sim/cancel/page.tsx
diff --git a/apps/portal/src/app/(portal)/subscriptions/[id]/sim/change-plan/page.tsx b/apps/portal/src/app/(authenticated)/subscriptions/[id]/sim/change-plan/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/subscriptions/[id]/sim/change-plan/page.tsx
rename to apps/portal/src/app/(authenticated)/subscriptions/[id]/sim/change-plan/page.tsx
diff --git a/apps/portal/src/app/(portal)/subscriptions/[id]/sim/reissue/page.tsx b/apps/portal/src/app/(authenticated)/subscriptions/[id]/sim/reissue/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/subscriptions/[id]/sim/reissue/page.tsx
rename to apps/portal/src/app/(authenticated)/subscriptions/[id]/sim/reissue/page.tsx
diff --git a/apps/portal/src/app/(portal)/subscriptions/[id]/sim/top-up/page.tsx b/apps/portal/src/app/(authenticated)/subscriptions/[id]/sim/top-up/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/subscriptions/[id]/sim/top-up/page.tsx
rename to apps/portal/src/app/(authenticated)/subscriptions/[id]/sim/top-up/page.tsx
diff --git a/apps/portal/src/app/(portal)/subscriptions/loading.tsx b/apps/portal/src/app/(authenticated)/subscriptions/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/subscriptions/loading.tsx
rename to apps/portal/src/app/(authenticated)/subscriptions/loading.tsx
diff --git a/apps/portal/src/app/(portal)/subscriptions/page.tsx b/apps/portal/src/app/(authenticated)/subscriptions/page.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/subscriptions/page.tsx
rename to apps/portal/src/app/(authenticated)/subscriptions/page.tsx
diff --git a/apps/portal/src/app/(portal)/support/cases/loading.tsx b/apps/portal/src/app/(authenticated)/support/cases/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/support/cases/loading.tsx
rename to apps/portal/src/app/(authenticated)/support/cases/loading.tsx
diff --git a/apps/portal/src/app/(authenticated)/support/cases/page.tsx b/apps/portal/src/app/(authenticated)/support/cases/page.tsx
new file mode 100644
index 00000000..54a27c29
--- /dev/null
+++ b/apps/portal/src/app/(authenticated)/support/cases/page.tsx
@@ -0,0 +1,5 @@
+import { SupportCasesView } from "@/features/support";
+
+export default function SupportCasesPage() {
+ return ;
+}
diff --git a/apps/portal/src/app/(portal)/support/new/loading.tsx b/apps/portal/src/app/(authenticated)/support/new/loading.tsx
similarity index 100%
rename from apps/portal/src/app/(portal)/support/new/loading.tsx
rename to apps/portal/src/app/(authenticated)/support/new/loading.tsx
diff --git a/apps/portal/src/app/(authenticated)/support/new/page.tsx b/apps/portal/src/app/(authenticated)/support/new/page.tsx
new file mode 100644
index 00000000..65c960da
--- /dev/null
+++ b/apps/portal/src/app/(authenticated)/support/new/page.tsx
@@ -0,0 +1,5 @@
+import { NewSupportCaseView } from "@/features/support";
+
+export default function NewSupportCasePage() {
+ return ;
+}
diff --git a/apps/portal/src/app/(portal)/support/new/page.tsx b/apps/portal/src/app/(portal)/support/new/page.tsx
deleted file mode 100644
index aa113065..00000000
--- a/apps/portal/src/app/(portal)/support/new/page.tsx
+++ /dev/null
@@ -1,260 +0,0 @@
-"use client";
-import { logger } from "@/core/config";
-
-import { useState } from "react";
-import { useRouter } from "next/navigation";
-import Link from "next/link";
-import {
- ArrowLeftIcon,
- PaperAirplaneIcon,
- ExclamationCircleIcon,
- InformationCircleIcon,
-} from "@heroicons/react/24/outline";
-
-export default function NewSupportCasePage() {
- const router = useRouter();
- const [isSubmitting, setIsSubmitting] = useState(false);
- const [formData, setFormData] = useState({
- subject: "",
- category: "Technical",
- priority: "Medium",
- description: "",
- });
-
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- setIsSubmitting(true);
-
- // Mock submission - would normally send to API
- void (async () => {
- try {
- await new Promise(resolve => setTimeout(resolve, 2000));
-
- // Redirect to cases list with success message
- router.push("/support/cases?created=true");
- } catch (error) {
- logger.error(error, "Error creating case");
- } finally {
- setIsSubmitting(false);
- }
- })();
- };
-
- const handleInputChange = (field: string, value: string) => {
- setFormData(prev => ({
- ...prev,
- [field]: value,
- }));
- };
-
- const isFormValid = formData.subject.trim() && formData.description.trim();
-
- return (
-
-
- {/* Header */}
-
-
-
-
-
-
-
Create Support Case
-
Get help from our support team
-
-
-
- {/* Help Tips */}
-
-
-
-
-
-
-
Before creating a case
-
-
- - Check our knowledge base for common solutions
- - Include relevant error messages or screenshots
- - Provide detailed steps to reproduce the issue
- - Mention your service or subscription if applicable
-
-
-
-
-
-
- {/* Form */}
-
-
- {/* Additional Help */}
-
-
Need immediate help?
-
-
-
Phone Support
-
- 9:30-18:00 JST
-
- 0120-660-470
-
-
-
-
Knowledge Base
-
- Search our help articles for quick solutions
-
-
- Browse Knowledge Base
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/portal/src/app/auth/forgot-password/page.tsx b/apps/portal/src/app/(public)/auth/forgot-password/page.tsx
similarity index 100%
rename from apps/portal/src/app/auth/forgot-password/page.tsx
rename to apps/portal/src/app/(public)/auth/forgot-password/page.tsx
diff --git a/apps/portal/src/app/auth/link-whmcs/page.tsx b/apps/portal/src/app/(public)/auth/link-whmcs/page.tsx
similarity index 100%
rename from apps/portal/src/app/auth/link-whmcs/page.tsx
rename to apps/portal/src/app/(public)/auth/link-whmcs/page.tsx
diff --git a/apps/portal/src/app/auth/loading.tsx b/apps/portal/src/app/(public)/auth/loading.tsx
similarity index 100%
rename from apps/portal/src/app/auth/loading.tsx
rename to apps/portal/src/app/(public)/auth/loading.tsx
diff --git a/apps/portal/src/app/auth/login/page.tsx b/apps/portal/src/app/(public)/auth/login/page.tsx
similarity index 100%
rename from apps/portal/src/app/auth/login/page.tsx
rename to apps/portal/src/app/(public)/auth/login/page.tsx
diff --git a/apps/portal/src/app/auth/reset-password/page.tsx b/apps/portal/src/app/(public)/auth/reset-password/page.tsx
similarity index 100%
rename from apps/portal/src/app/auth/reset-password/page.tsx
rename to apps/portal/src/app/(public)/auth/reset-password/page.tsx
diff --git a/apps/portal/src/app/auth/set-password/page.tsx b/apps/portal/src/app/(public)/auth/set-password/page.tsx
similarity index 100%
rename from apps/portal/src/app/auth/set-password/page.tsx
rename to apps/portal/src/app/(public)/auth/set-password/page.tsx
diff --git a/apps/portal/src/app/auth/signup/page.tsx b/apps/portal/src/app/(public)/auth/signup/page.tsx
similarity index 100%
rename from apps/portal/src/app/auth/signup/page.tsx
rename to apps/portal/src/app/(public)/auth/signup/page.tsx
diff --git a/apps/portal/src/app/(public)/loading.tsx b/apps/portal/src/app/(public)/loading.tsx
new file mode 100644
index 00000000..51bac016
--- /dev/null
+++ b/apps/portal/src/app/(public)/loading.tsx
@@ -0,0 +1,5 @@
+import { PublicLandingLoadingView } from "@/features/marketing";
+
+export default function PublicHomeLoading() {
+ return ;
+}
diff --git a/apps/portal/src/app/(public)/page.tsx b/apps/portal/src/app/(public)/page.tsx
new file mode 100644
index 00000000..f54fd694
--- /dev/null
+++ b/apps/portal/src/app/(public)/page.tsx
@@ -0,0 +1,5 @@
+import { PublicLandingView } from "@/features/marketing";
+
+export default function PublicHomePage() {
+ return ;
+}
diff --git a/apps/portal/src/app/page.tsx b/apps/portal/src/app/page.tsx
deleted file mode 100644
index 9148c59f..00000000
--- a/apps/portal/src/app/page.tsx
+++ /dev/null
@@ -1,288 +0,0 @@
-import Link from "next/link";
-import { Logo } from "@/components/ui/logo";
-import {
- ArrowPathIcon,
- UserIcon,
- SparklesIcon,
- CreditCardIcon,
- Cog6ToothIcon,
- PhoneIcon,
- ChartBarIcon,
-} from "@heroicons/react/24/outline";
-
-export default function Home() {
- return (
-
- {/* Header */}
-
-
-
-
-
-
-
Assist Solutions
-
Customer Portal
-
-
-
-
-
- Login
-
-
- Support
-
-
-
-
-
-
- {/* Hero Section */}
-
- {/* Abstract background elements */}
-
-
-
-
-
- New Assist Solutions Customer Portal
-
-
-
-
-
-
-
- {/* Customer Portal Access Section */}
-
-
-
-
Access Your Portal
-
Choose the option that applies to you
-
-
-
- {/* Existing Customers - Migration */}
-
-
-
-
Existing Customers
-
- Migrate to our new portal and enjoy enhanced security with modern interface.
-
-
-
-
- Migrate Your Account
-
-
Takes just a few minutes
-
-
-
-
- {/* Portal Users */}
-
-
-
-
-
-
Portal Users
-
- Sign in to access your dashboard and manage all your services efficiently.
-
-
-
-
- Login to Portal
-
-
Secure access to your account
-
-
-
-
- {/* New Customers */}
-
-
-
-
-
-
New Customers
-
- Create your account and access our full range of IT solutions and services.
-
-
-
-
- Create Account
-
-
Start your journey with us
-
-
-
-
-
-
-
- {/* Portal Features Section */}
-
-
-
-
Portal Features
-
- Everything you need to manage your Assist Solutions services
-
-
-
-
-
-
-
-
-
Billing & Payments
-
- View invoices, payment history, and manage billing
-
-
-
-
-
-
-
-
Service Management
-
Control and configure your active services
-
-
-
-
-
Support Tickets
-
Create and track support requests
-
-
-
-
-
-
-
Usage Reports
-
Monitor service usage and performance
-
-
-
-
-
- {/* Support Section */}
-
-
-
-
Need Help?
-
Our support team is here to assist you
-
-
-
- {/* Contact Details Box */}
-
-
Contact Details
-
-
-
Phone Support
-
9:30-18:00 JST
-
0120-660-470
-
Toll Free within Japan
-
-
-
-
Email Support
-
Response within 24h
-
- Send Message
-
-
-
-
-
- {/* Live Chat & Business Hours Box */}
-
-
- Live Chat & Business Hours
-
-
-
-
Live Chat
-
Available 24/7
-
- Start Chat
-
-
-
-
-
Business Hours
-
-
- Monday - Saturday:
-
- 10:00 AM - 6:00 PM JST
-
-
- Sunday:
-
- Closed
-
-
-
-
-
-
-
-
-
- {/* Footer */}
-
-
- );
-}
diff --git a/apps/portal/src/features/dashboard/index.ts b/apps/portal/src/features/dashboard/index.ts
index a234113b..367801e4 100644
--- a/apps/portal/src/features/dashboard/index.ts
+++ b/apps/portal/src/features/dashboard/index.ts
@@ -1,2 +1,3 @@
export * from "./components";
export * from "./hooks";
+export * from "./views";
diff --git a/apps/portal/src/app/(portal)/dashboard/page.tsx b/apps/portal/src/features/dashboard/views/DashboardView.tsx
similarity index 90%
rename from apps/portal/src/app/(portal)/dashboard/page.tsx
rename to apps/portal/src/features/dashboard/views/DashboardView.tsx
index 58e59c33..7963c369 100644
--- a/apps/portal/src/app/(portal)/dashboard/page.tsx
+++ b/apps/portal/src/features/dashboard/views/DashboardView.tsx
@@ -1,23 +1,16 @@
"use client";
+
import { useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
-import { useAuthStore } from "@/features/auth/services/auth.store";
-import { useDashboardSummary } from "@/features/dashboard/hooks/useDashboard";
-
-import type { Activity } from "@customer-portal/domain";
+import type { Activity, DashboardSummary } from "@customer-portal/domain";
import {
- CreditCardIcon,
ServerIcon,
ChatBubbleLeftRightIcon,
- ExclamationTriangleIcon,
ChevronRightIcon,
- PlusIcon,
DocumentTextIcon,
ArrowTrendingUpIcon,
CalendarDaysIcon,
- BellIcon,
- ClipboardDocumentListIcon,
} from "@heroicons/react/24/outline";
import {
CreditCardIcon as CreditCardIconSolid,
@@ -26,15 +19,19 @@ import {
ClipboardDocumentListIcon as ClipboardDocumentListIconSolid,
} from "@heroicons/react/24/solid";
import { format, formatDistanceToNow } from "date-fns";
+
+import { useAuthStore } from "@/features/auth/services/auth.store";
+import { useDashboardSummary } from "@/features/dashboard/hooks";
import { StatCard, QuickAction, DashboardActivityItem } from "@/features/dashboard/components";
import { LoadingSpinner } from "@/components/ui/loading-skeleton";
import { ErrorState } from "@/components/ui/error-state";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
-export default function DashboardPage() {
+export function DashboardView() {
const router = useRouter();
const { user, isAuthenticated, loading: authLoading } = useAuthStore();
const { data: summary, isLoading: summaryLoading, error } = useDashboardSummary();
+ const upcomingInvoice = summary?.nextInvoice ?? null;
const [paymentLoading, setPaymentLoading] = useState(false);
const [paymentError, setPaymentError] = useState(null);
@@ -115,7 +112,7 @@ export default function DashboardPage() {
)?.recentOrders as number) || 0}
+ value={summary?.stats?.recentOrders ?? 0}
icon={ClipboardDocumentListIconSolid}
gradient="from-gray-500 to-gray-600"
href="/orders"
@@ -157,7 +154,7 @@ export default function DashboardPage() {
{/* Main Content Area */}
{/* Upcoming Payment - compressed attention banner */}
- {summary?.nextInvoice && (
+ {upcomingInvoice && (
Upcoming Payment
•
- Invoice #{summary.nextInvoice.id}
+ Invoice #{upcomingInvoice.id}
•
-
+
Due{" "}
- {formatDistanceToNow(new Date(summary.nextInvoice.dueDate), {
+ {formatDistanceToNow(new Date(upcomingInvoice.dueDate), {
addSuffix: true,
})}
- {formatCurrency(summary.nextInvoice.amount, {
- currency: summary.nextInvoice.currency || "JPY",
- locale: getCurrencyLocale(summary.nextInvoice.currency || "JPY"),
+ {formatCurrency(upcomingInvoice.amount, {
+ currency: upcomingInvoice.currency || "JPY",
+ locale: getCurrencyLocale(upcomingInvoice.currency || "JPY"),
})}
- Exact due date:{" "}
- {format(new Date(summary.nextInvoice.dueDate), "MMMM d, yyyy")}
+ Exact due date: {format(new Date(upcomingInvoice.dueDate), "MMMM d, yyyy")}
View invoice
@@ -276,12 +272,13 @@ export default function DashboardPage() {
}
// Helpers and small components (local to dashboard)
-function truncateName(name: string, len = 28) {
- if (name.length <= len) return name;
- return name.slice(0, Math.max(0, len - 1)) + "…";
-}
-
-function TasksChip({ summaryLoading, summary }: { summaryLoading: boolean; summary: any }) {
+function TasksChip({
+ summaryLoading,
+ summary,
+}: {
+ summaryLoading: boolean;
+ summary: DashboardSummary | undefined;
+}) {
const router = useRouter();
if (summaryLoading) return null;
const tasks: Array<{ label: string; href: string }> = [];
@@ -339,7 +336,11 @@ function RecentActivityCard({
diff --git a/apps/portal/src/features/dashboard/views/index.ts b/apps/portal/src/features/dashboard/views/index.ts
new file mode 100644
index 00000000..bdd37ab9
--- /dev/null
+++ b/apps/portal/src/features/dashboard/views/index.ts
@@ -0,0 +1 @@
+export * from "./DashboardView";
diff --git a/apps/portal/src/features/index.ts b/apps/portal/src/features/index.ts
index 0faed348..bcc4ac7c 100644
--- a/apps/portal/src/features/index.ts
+++ b/apps/portal/src/features/index.ts
@@ -1,3 +1,5 @@
export * as billing from "./billing";
export * as subscriptions from "./subscriptions";
export * as dashboard from "./dashboard";
+export * as marketing from "./marketing";
+export * as support from "./support";
diff --git a/apps/portal/src/features/marketing/index.ts b/apps/portal/src/features/marketing/index.ts
new file mode 100644
index 00000000..a738deb4
--- /dev/null
+++ b/apps/portal/src/features/marketing/index.ts
@@ -0,0 +1,2 @@
+export * from "./views/PublicLandingView";
+export * from "./views/PublicLandingLoadingView";
diff --git a/apps/portal/src/app/loading.tsx b/apps/portal/src/features/marketing/views/PublicLandingLoadingView.tsx
similarity index 85%
rename from apps/portal/src/app/loading.tsx
rename to apps/portal/src/features/marketing/views/PublicLandingLoadingView.tsx
index b4e396ee..70765831 100644
--- a/apps/portal/src/app/loading.tsx
+++ b/apps/portal/src/features/marketing/views/PublicLandingLoadingView.tsx
@@ -1,6 +1,6 @@
import { Skeleton } from "@/components/ui/loading-skeleton";
-export default function RootLoading() {
+export function PublicLandingLoadingView() {
return (