feat(web): Phase 11.3 響應式設計 - Mobile/Tablet 支援
ConversationalView 響應式改造: - Mobile: 全屏詳情面板 + 返回按鈕 - Tablet: 64px 側邊欄 (w-64) - Desktop: 80px 側邊欄 (w-80) - i18n: backToList 翻譯 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -263,6 +263,7 @@
|
||||
"fetchError": "Failed to fetch approvals",
|
||||
"noPendingApprovals": "No pending approvals",
|
||||
"selectApproval": "Select an approval to view details",
|
||||
"backToList": "Back to list",
|
||||
"previousApproval": "Previous",
|
||||
"nextApproval": "Next",
|
||||
"holdToApproveHint": "Hold button to approve or reject",
|
||||
|
||||
@@ -263,6 +263,7 @@
|
||||
"fetchError": "無法取得授權清單",
|
||||
"noPendingApprovals": "目前無待授權項目",
|
||||
"selectApproval": "請選擇一個待授權項目",
|
||||
"backToList": "返回列表",
|
||||
"previousApproval": "上一個項目",
|
||||
"nextApproval": "下一個項目",
|
||||
"holdToApproveHint": "長按按鈕以批准或拒絕",
|
||||
|
||||
@@ -70,6 +70,8 @@ export function ConversationalView({
|
||||
// 選中的 Approval
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const [showShortcuts, setShowShortcuts] = useState(false)
|
||||
// Phase 11.3: Mobile 詳情面板狀態
|
||||
const [showMobileDetail, setShowMobileDetail] = useState(false)
|
||||
|
||||
// 轉換為前端格式
|
||||
const approvals = useMemo(() => {
|
||||
@@ -99,9 +101,17 @@ export function ConversationalView({
|
||||
} else {
|
||||
setSelectedId(null)
|
||||
}
|
||||
// Phase 11.3: 關閉 Mobile 詳情面板
|
||||
setShowMobileDetail(false)
|
||||
}
|
||||
}, [approvals, selectedId])
|
||||
|
||||
// Phase 11.3: Mobile 選擇處理
|
||||
const handleMobileSelect = useCallback((id: string) => {
|
||||
setSelectedId(id)
|
||||
setShowMobileDetail(true)
|
||||
}, [])
|
||||
|
||||
// 導航函數
|
||||
const goToPrev = useCallback(() => {
|
||||
if (selectedIndex > 0) {
|
||||
@@ -146,8 +156,15 @@ export function ConversationalView({
|
||||
|
||||
return (
|
||||
<div className={cn('flex h-full bg-nothing-gray-50', className)}>
|
||||
{/* 左側: 對話列表 */}
|
||||
<div className="w-80 flex-shrink-0 border-r border-nothing-gray-200 bg-white flex flex-col">
|
||||
{/* 左側: 對話列表 - 響應式寬度 */}
|
||||
{/* Mobile: 全寬 | Tablet: w-64 | Desktop: w-80 */}
|
||||
<div className={cn(
|
||||
'flex-shrink-0 border-r border-nothing-gray-200 bg-white flex flex-col',
|
||||
// Phase 11.3: 響應式寬度
|
||||
'w-full md:w-64 lg:w-80',
|
||||
// Mobile: 詳情面板打開時隱藏列表
|
||||
showMobileDetail && 'hidden md:flex'
|
||||
)}>
|
||||
{/* 列表 Header */}
|
||||
<div className="flex-shrink-0 p-4 border-b border-nothing-gray-100">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -235,18 +252,39 @@ export function ConversationalView({
|
||||
key={approval.id}
|
||||
approval={approval}
|
||||
isSelected={approval.id === selectedId}
|
||||
onSelect={() => setSelectedId(approval.id)}
|
||||
onSelect={() => handleMobileSelect(approval.id)}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右側: 詳情面板 */}
|
||||
<div className="flex-1 flex flex-col overflow-hidden relative">
|
||||
{/* 右側: 詳情面板 - 響應式 */}
|
||||
{/* Mobile: 全屏覆蓋 | Tablet/Desktop: 右側面板 */}
|
||||
<div className={cn(
|
||||
'flex flex-col overflow-hidden relative',
|
||||
// Phase 11.3: Mobile 全屏覆蓋模式
|
||||
showMobileDetail
|
||||
? 'fixed inset-0 z-40 bg-nothing-gray-50 md:relative md:inset-auto md:z-auto md:flex-1'
|
||||
: 'hidden md:flex md:flex-1'
|
||||
)}>
|
||||
{/* Mobile 返回按鈕 */}
|
||||
<div className="flex-shrink-0 flex items-center justify-between p-4 border-b border-nothing-gray-200 bg-white md:hidden">
|
||||
<button
|
||||
onClick={() => setShowMobileDetail(false)}
|
||||
className="flex items-center gap-2 text-claw-blue hover:text-claw-blue/80 transition-colors"
|
||||
>
|
||||
<ChevronLeft className="w-5 h-5" />
|
||||
<span className="text-sm font-medium">{t('backToList')}</span>
|
||||
</button>
|
||||
<div className="flex items-center gap-1 px-2 py-1 rounded-lg text-xs font-mono bg-nothing-gray-100">
|
||||
<span>{selectedIndex + 1} / {approvals.length}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Y 鍵長按進度指示器 */}
|
||||
{isYKeyHolding && (
|
||||
<div className="absolute top-0 left-0 right-0 h-1 bg-nothing-gray-200 z-10">
|
||||
<div className="absolute top-0 left-0 right-0 h-1 bg-nothing-gray-200 z-10 md:top-0">
|
||||
<div
|
||||
className="h-full bg-status-healthy transition-all duration-100"
|
||||
style={{ width: `${yKeyProgress}%` }}
|
||||
@@ -255,7 +293,7 @@ export function ConversationalView({
|
||||
)}
|
||||
|
||||
{selectedApproval ? (
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
<div className="flex-1 overflow-y-auto p-4 md:p-6">
|
||||
<ApprovalCard
|
||||
request={selectedApproval}
|
||||
onApprove={handleApprove}
|
||||
|
||||
Reference in New Issue
Block a user