diff --git a/apps/web/src/app/[locale]/apm/page.tsx b/apps/web/src/app/[locale]/apm/page.tsx
new file mode 100644
index 000000000..32d4dbe9a
--- /dev/null
+++ b/apps/web/src/app/[locale]/apm/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * APM Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { Activity } from 'lucide-react'
+
+export default function ApmPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/apps/page.tsx b/apps/web/src/app/[locale]/apps/page.tsx
new file mode 100644
index 000000000..0c84b851d
--- /dev/null
+++ b/apps/web/src/app/[locale]/apps/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 應用 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { AppWindow } from 'lucide-react'
+
+export default function AppsPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/auto-repair/page.tsx b/apps/web/src/app/[locale]/auto-repair/page.tsx
new file mode 100644
index 000000000..159902588
--- /dev/null
+++ b/apps/web/src/app/[locale]/auto-repair/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 自動修復 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { Wrench } from 'lucide-react'
+
+export default function AutoRepairPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/billing/page.tsx b/apps/web/src/app/[locale]/billing/page.tsx
new file mode 100644
index 000000000..ab9728614
--- /dev/null
+++ b/apps/web/src/app/[locale]/billing/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 帳單 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { CreditCard } from 'lucide-react'
+
+export default function BillingPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/compliance/page.tsx b/apps/web/src/app/[locale]/compliance/page.tsx
new file mode 100644
index 000000000..3248d4715
--- /dev/null
+++ b/apps/web/src/app/[locale]/compliance/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 合規 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { ClipboardCheck } from 'lucide-react'
+
+export default function CompliancePage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/cost/page.tsx b/apps/web/src/app/[locale]/cost/page.tsx
new file mode 100644
index 000000000..0ae8c04c7
--- /dev/null
+++ b/apps/web/src/app/[locale]/cost/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 成本分析 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { DollarSign } from 'lucide-react'
+
+export default function CostPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/deployments/page.tsx b/apps/web/src/app/[locale]/deployments/page.tsx
new file mode 100644
index 000000000..e11974155
--- /dev/null
+++ b/apps/web/src/app/[locale]/deployments/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 部署管理 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { Package } from 'lucide-react'
+
+export default function DeploymentsPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/help/page.tsx b/apps/web/src/app/[locale]/help/page.tsx
new file mode 100644
index 000000000..ac59b47fe
--- /dev/null
+++ b/apps/web/src/app/[locale]/help/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 說明文件 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { HelpCircle } from 'lucide-react'
+
+export default function HelpPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/monitoring/page.tsx b/apps/web/src/app/[locale]/monitoring/page.tsx
new file mode 100644
index 000000000..2f2b60328
--- /dev/null
+++ b/apps/web/src/app/[locale]/monitoring/page.tsx
@@ -0,0 +1,265 @@
+'use client'
+
+/**
+ * Monitoring Page - 服務監控
+ * ===========================
+ * 黃金指標 (Gold Metrics) + 四主機狀態 + 服務健康
+ * 資料來源:
+ * GET /api/v1/metrics/gold
+ * GET /api/v1/dashboard/hosts
+ * GET /api/v1/dashboard (SSE 快照)
+ *
+ * @created 2026-04-01 ogt
+ */
+
+import { useState, useEffect, useCallback, useRef } from 'react'
+import { useTranslations } from 'next-intl'
+import { AppLayout } from '@/components/layout'
+import { useGlobalPulseMetrics } from '@/hooks/useGlobalPulseMetrics'
+import { GlobalPulseChart } from '@/components/charts/global-pulse-chart'
+import { HostGrid } from '@/components/infra/host-grid'
+import { cn } from '@/lib/utils'
+import {
+ Monitor, RefreshCw, AlertCircle,
+ CheckCircle2, XCircle, Minus,
+} from 'lucide-react'
+
+// =============================================================================
+// Types
+// =============================================================================
+
+interface ServiceHealth {
+ name: string
+ status: 'healthy' | 'warning' | 'critical' | 'unknown'
+ latency_ms: number | null
+ uptime_pct: number | null
+ last_checked: string
+}
+
+interface DashboardResponse {
+ healthy_count: number
+ warning_count: number
+ critical_count: number
+ total_count: number
+ services: ServiceHealth[]
+ timestamp: string
+}
+
+// =============================================================================
+// Helpers
+// =============================================================================
+
+const getApiBaseUrl = () => {
+ if (typeof window === 'undefined') return ''
+ const url = process.env.NEXT_PUBLIC_API_URL
+ if (!url) console.error('[AWOOOI ERROR] Missing NEXT_PUBLIC_API_URL') // eslint-disable-line no-console
+ return url ?? ''
+}
+
+const STATUS_ICON = {
+ healthy: ,
+ warning: ,
+ critical: ,
+ unknown: ,
+}
+
+const STATUS_BADGE = {
+ healthy: 'bg-status-healthy/10 text-status-healthy border-status-healthy/20',
+ warning: 'bg-status-warning/10 text-status-warning border-status-warning/20',
+ critical: 'bg-status-critical/10 text-status-critical border-status-critical/20',
+ unknown: 'bg-nothing-gray-100 text-nothing-gray-500 border-nothing-gray-200',
+}
+
+// =============================================================================
+// Sub-component: Health Summary
+// =============================================================================
+
+function HealthSummary({ data }: { data: DashboardResponse }) {
+ const total = data.total_count || 1
+ return (
+
+ {[
+ { label: '正常', count: data.healthy_count, color: 'text-status-healthy', bg: 'bg-status-healthy/10' },
+ { label: '警告', count: data.warning_count, color: 'text-status-warning', bg: 'bg-status-warning/10' },
+ { label: '異常', count: data.critical_count, color: 'text-status-critical', bg: 'bg-status-critical/10' },
+ ].map(item => (
+
+
+ {item.count}
+
+
+ {item.label} · {Math.round(item.count / total * 100)}%
+
+
+ ))}
+
+ )
+}
+
+// =============================================================================
+// Page
+// =============================================================================
+
+export default function MonitoringPage({ params }: { params: { locale: string } }) {
+ const t = useTranslations()
+
+ const { metrics, isLoading: metricsLoading } = useGlobalPulseMetrics({
+ pollInterval: 30000,
+ enablePolling: true,
+ })
+
+ const [dashboard, setDashboard] = useState(null)
+ const [dashLoading, setDashLoading] = useState(true)
+ const [dashError, setDashError] = useState(null)
+ const abortRef = useRef(null)
+
+ const fetchDashboard = useCallback(async () => {
+ const base = getApiBaseUrl()
+ if (!base) return
+
+ abortRef.current?.abort()
+ const ctrl = new AbortController()
+ abortRef.current = ctrl
+
+ setDashLoading(true)
+ setDashError(null)
+ try {
+ const res = await fetch(`${base}/api/v1/dashboard`, { signal: ctrl.signal })
+ if (!res.ok) throw new Error(`HTTP ${res.status}`)
+ setDashboard(await res.json())
+ } catch (e) {
+ if (e instanceof Error && e.name === 'AbortError') return
+ setDashError(e instanceof Error ? e.message : 'Unknown error')
+ } finally {
+ setDashLoading(false)
+ }
+ }, [])
+
+ useEffect(() => {
+ fetchDashboard()
+ const id = setInterval(fetchDashboard, 30000)
+ return () => {
+ clearInterval(id)
+ abortRef.current?.abort()
+ }
+ }, [fetchDashboard])
+
+ const isLoading = metricsLoading || dashLoading
+
+ return (
+
+ {/* Header */}
+
+
+
+
+ {t('nav.monitoring')}
+
+
+ {t('alerts.autoRefresh', { seconds: 30 })}
+
+
+
+
+
+ {/* Error */}
+ {dashError && (
+
+ )}
+
+ {/* Health Summary */}
+ {dashboard && }
+
+ {/* Gold Metrics */}
+
+
+ 黃金指標 (Gold Metrics)
+
+ {metricsLoading && metrics.length === 0 ? (
+
+
+
+ ) : (
+
+ )}
+
+
+ {/* Host Grid */}
+
+
+ 主機狀態 (Four-Host Architecture)
+
+
+
+
+ {/* Service Table */}
+ {dashboard && dashboard.services && dashboard.services.length > 0 && (
+
+
+ 服務清單
+
+
+
+
+
+ {['服務名稱', '狀態', '延遲', '可用率', '最後檢查'].map(h => (
+ |
+ {h}
+ |
+ ))}
+
+
+
+ {dashboard.services.map((svc, i) => (
+
+ |
+ {svc.name}
+ |
+
+
+ {STATUS_ICON[svc.status]}
+ {svc.status}
+
+ |
+
+ {svc.latency_ms != null ? `${svc.latency_ms}ms` : '--'}
+ |
+
+ {svc.uptime_pct != null ? `${svc.uptime_pct.toFixed(2)}%` : '--'}
+ |
+
+ {svc.last_checked
+ ? new Date(svc.last_checked).toLocaleTimeString()
+ : '--'}
+ |
+
+ ))}
+
+
+
+
+ )}
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/notifications/page.tsx b/apps/web/src/app/[locale]/notifications/page.tsx
new file mode 100644
index 000000000..9b0200d6e
--- /dev/null
+++ b/apps/web/src/app/[locale]/notifications/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 通知設定 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { BellRing } from 'lucide-react'
+
+export default function NotificationsPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/reports/page.tsx b/apps/web/src/app/[locale]/reports/page.tsx
new file mode 100644
index 000000000..9f1969f98
--- /dev/null
+++ b/apps/web/src/app/[locale]/reports/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 報表 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { FileText } from 'lucide-react'
+
+export default function ReportsPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/security/page.tsx b/apps/web/src/app/[locale]/security/page.tsx
new file mode 100644
index 000000000..600fdb666
--- /dev/null
+++ b/apps/web/src/app/[locale]/security/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 安全 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { Shield } from 'lucide-react'
+
+export default function SecurityPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/services/page.tsx b/apps/web/src/app/[locale]/services/page.tsx
new file mode 100644
index 000000000..722a6786c
--- /dev/null
+++ b/apps/web/src/app/[locale]/services/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 服務目錄 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { Server } from 'lucide-react'
+
+export default function ServicesPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/tickets/page.tsx b/apps/web/src/app/[locale]/tickets/page.tsx
new file mode 100644
index 000000000..ad62ef18e
--- /dev/null
+++ b/apps/web/src/app/[locale]/tickets/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 工單 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { Ticket } from 'lucide-react'
+
+export default function TicketsPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/topology/page.tsx b/apps/web/src/app/[locale]/topology/page.tsx
new file mode 100644
index 000000000..ab4fc8d3e
--- /dev/null
+++ b/apps/web/src/app/[locale]/topology/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 拓撲圖 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { GitBranch } from 'lucide-react'
+
+export default function TopologyPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/app/[locale]/users/page.tsx b/apps/web/src/app/[locale]/users/page.tsx
new file mode 100644
index 000000000..a86452449
--- /dev/null
+++ b/apps/web/src/app/[locale]/users/page.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+/**
+ * 使用者管理 Page
+ * @created 2026-04-01 ogt - 路由佔位 (awaiting implementation)
+ */
+
+import { AppLayout } from '@/components/layout'
+import { ComingSoon } from '@/components/layout'
+import { Users } from 'lucide-react'
+
+export default function UsersPage({ params }: { params: { locale: string } }) {
+ return (
+
+
+
+ )
+}
diff --git a/apps/web/src/components/layout/coming-soon.tsx b/apps/web/src/components/layout/coming-soon.tsx
new file mode 100644
index 000000000..f8c26fdac
--- /dev/null
+++ b/apps/web/src/components/layout/coming-soon.tsx
@@ -0,0 +1,52 @@
+'use client'
+
+/**
+ * ComingSoon - 統一的「建設中」佔位元件
+ * ======================================
+ * 用於尚未實作的路由頁面
+ * Nothing.tech 極簡風格
+ *
+ * @created 2026-04-01 ogt
+ */
+
+import { type LucideIcon } from 'lucide-react'
+import { cn } from '@/lib/utils'
+
+interface ComingSoonProps {
+ icon?: LucideIcon
+ title: string
+ description?: string
+ className?: string
+}
+
+export function ComingSoon({ icon: Icon, title, description, className }: ComingSoonProps) {
+ return (
+
+ {Icon && (
+
+
+
+ )}
+
+ {title}
+
+ {description && (
+
+ {description}
+
+ )}
+
+ {[0, 1, 2].map(i => (
+
+ ))}
+
+
+ )
+}
diff --git a/apps/web/src/components/layout/index.ts b/apps/web/src/components/layout/index.ts
index cd3f87521..56018d367 100644
--- a/apps/web/src/components/layout/index.ts
+++ b/apps/web/src/components/layout/index.ts
@@ -7,3 +7,4 @@
export { Sidebar } from './sidebar'
export { Header } from './header'
export { AppLayout } from './app-layout'
+export { ComingSoon } from './coming-soon'