[CLAUDE] Contract: GD3-D cay toan trinh vao Panel-3 (slot 63 owner chot D + "mo rong" = ca 3 trang) + prop bodyMaxHClass (gate F-1)

- Owner @S168: (63)(b)=D cay-thay-Panel-3 · (a) "cu mo rong" -> ca 3 trang 3-panel {fe-user MyContractsPage + InboxPage · fe-admin ContractsListPage}; WorkflowMatrixViewPage KHONG ap (trang bang full-width 0-Panel-3 — khai chu dich, gate F-8 phan lead DUNG)
- Panel-3 khi CHUA chon HD: PipelineTreePanel currentStage=3 the cho o trong "Quy trinh duyet se hien khi chon HD"; chon HD -> nhuong cho WorkflowHistoryPanel (trade-off D khai)
- Gate PASS-WITH-FLAGS-10 (1 MAJOR/6 MINOR/3 INFO, 0 blocker) — disposition tung dong trong sub-reviewer-gate-gd3d.md
- F-1 MAJOR va: khuon +prop bodyMaxHClass (default GIU NGUYEN — 3 host giay-cuon cu 0 doi), 3 host Panel-3 pass lg:max-h-none => panel giu dung 1 thanh cuon (max-h khuon vuot cho /inbox ~26px -> 2 cuon long) — mirror x2 app SHA-pair 7baf6247
- Go X unused import 3 trang (control-am grep co rang)
- build x2 PASS · suite 642/0 (45D+597I)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-08-01 16:49:46 +07:00
parent a2b37dcccc
commit 081557b441
5 changed files with 39 additions and 23 deletions

View File

@ -86,9 +86,15 @@ function buildTree(rows: PeListItem[]): YearNode[] {
export function PipelineTreePanel({ export function PipelineTreePanel({
currentStage, currentStage,
className, className,
bodyMaxHClass,
}: { }: {
currentStage: PipelineStageNo currentStage: PipelineStageNo
className?: string className?: string
// [S168 GĐ3-D gate F-1] Host 3-panel (Panel-3 TỰ CUỘN) truyền class trần riêng cho THÂN
// cây (vd `lg:max-h-none` — panel giữ đúng 1 thanh cuộn; max-h mặc định 100vh16rem là
// hiệu-chỉnh cho trang giấy-cuộn, vượt chỗ Panel-3 /inbox ~26px ⇒ 2 cuộn lồng nhau).
// Mặc định giữ nguyên — 3 host giấy-cuộn cũ không đổi hành vi.
bodyMaxHClass?: string
}) { }) {
const { canPe, buildStages } = usePipelineStages() const { canPe, buildStages } = usePipelineStages()
const [search, setSearch] = useState('') const [search, setSearch] = useState('')
@ -212,7 +218,7 @@ export function PipelineTreePanel({
/> />
</div> </div>
<div className="max-h-[28rem] overflow-y-auto lg:max-h-[calc(100vh-16rem)]"> <div className={cn('overflow-y-auto', bodyMaxHClass ?? 'max-h-[28rem] lg:max-h-[calc(100vh-16rem)]')}>
{!canPe && ( {!canPe && (
<p className="px-3 py-4 text-[12px] text-slate-500"> <p className="px-3 py-4 text-[12px] text-slate-500">
Bạn chưa quyền xem phiếu Duyệt NCC nên cây toàn trình chưa hiển thị đưc. Bạn chưa quyền xem phiếu Duyệt NCC nên cây toàn trình chưa hiển thị đưc.

View File

@ -14,9 +14,10 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import { useNavigate, useSearchParams } from 'react-router-dom' import { useNavigate, useSearchParams } from 'react-router-dom'
import { FileText, Search, X } from 'lucide-react' import { FileText, Search } from 'lucide-react'
import { ContractDetailContent } from '@/components/contracts/ContractDetailContent' import { ContractDetailContent } from '@/components/contracts/ContractDetailContent'
import { WorkflowHistoryPanel } from '@/components/contracts/WorkflowHistoryPanel' import { WorkflowHistoryPanel } from '@/components/contracts/WorkflowHistoryPanel'
import { PipelineTreePanel } from '@/components/pipeline/PipelineTreePanel'
import { PhaseBadge } from '@/components/PhaseBadge' import { PhaseBadge } from '@/components/PhaseBadge'
import { SlaTimer } from '@/components/SlaTimer' import { SlaTimer } from '@/components/SlaTimer'
import { EmptyState } from '@/components/EmptyState' import { EmptyState } from '@/components/EmptyState'
@ -240,12 +241,12 @@ export function ContractsListPage() {
{/* Panel 3 — Workflow + history */} {/* Panel 3 — Workflow + history */}
<aside className="hidden overflow-y-auto border-l border-slate-200 bg-white p-4 lg:block"> <aside className="hidden overflow-y-auto border-l border-slate-200 bg-white p-4 lg:block">
{!selectedId && ( {/* [S168 GĐ3-D — owner slot (63) chốt D] Chưa chọn HĐ ⇒ cây toàn trình (GĐ3)
<div className="rounded-lg border border-dashed border-slate-200 p-6 text-center text-sm text-slate-400"> thế chỗ ô trống; chọn HĐ thì nhường chỗ cho quy trình duyệt. Trade-off D
<X className="mx-auto mb-2 h-5 w-5" /> (cây rời panel khi chọn HĐ) — sub-implfe-gd3.md §1. */}
Quy trình duyệt sẽ hiện khi chọn . {/* [gate F-1/F-2] Panel-3 TỰ CUỘN ⇒ thân cây `lg:max-h-none` giữ đúng 1 thanh cuộn;
</div> sticky/self-start của khuôn vô hiệu ở host display:block này — by-design. */}
)} {!selectedId && <PipelineTreePanel currentStage={3} bodyMaxHClass="max-h-[28rem] lg:max-h-none" />}
{selectedId && detail.data && <WorkflowHistoryPanel contract={detail.data} />} {selectedId && detail.data && <WorkflowHistoryPanel contract={detail.data} />}
</aside> </aside>
</div> </div>

View File

@ -86,9 +86,15 @@ function buildTree(rows: PeListItem[]): YearNode[] {
export function PipelineTreePanel({ export function PipelineTreePanel({
currentStage, currentStage,
className, className,
bodyMaxHClass,
}: { }: {
currentStage: PipelineStageNo currentStage: PipelineStageNo
className?: string className?: string
// [S168 GĐ3-D gate F-1] Host 3-panel (Panel-3 TỰ CUỘN) truyền class trần riêng cho THÂN
// cây (vd `lg:max-h-none` — panel giữ đúng 1 thanh cuộn; max-h mặc định 100vh16rem là
// hiệu-chỉnh cho trang giấy-cuộn, vượt chỗ Panel-3 /inbox ~26px ⇒ 2 cuộn lồng nhau).
// Mặc định giữ nguyên — 3 host giấy-cuộn cũ không đổi hành vi.
bodyMaxHClass?: string
}) { }) {
const { canPe, buildStages } = usePipelineStages() const { canPe, buildStages } = usePipelineStages()
const [search, setSearch] = useState('') const [search, setSearch] = useState('')
@ -212,7 +218,7 @@ export function PipelineTreePanel({
/> />
</div> </div>
<div className="max-h-[28rem] overflow-y-auto lg:max-h-[calc(100vh-16rem)]"> <div className={cn('overflow-y-auto', bodyMaxHClass ?? 'max-h-[28rem] lg:max-h-[calc(100vh-16rem)]')}>
{!canPe && ( {!canPe && (
<p className="px-3 py-4 text-[12px] text-slate-500"> <p className="px-3 py-4 text-[12px] text-slate-500">
Bạn chưa quyền xem phiếu Duyệt NCC nên cây toàn trình chưa hiển thị đưc. Bạn chưa quyền xem phiếu Duyệt NCC nên cây toàn trình chưa hiển thị đưc.

View File

@ -7,9 +7,10 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import { useNavigate, useSearchParams } from 'react-router-dom' import { useNavigate, useSearchParams } from 'react-router-dom'
import { Inbox, AlertTriangle, Clock, FileText, Search, X, ClipboardList } from 'lucide-react' import { Inbox, AlertTriangle, Clock, FileText, Search, ClipboardList } from 'lucide-react'
import { ContractDetailContent } from '@/components/contracts/ContractDetailContent' import { ContractDetailContent } from '@/components/contracts/ContractDetailContent'
import { WorkflowHistoryPanel } from '@/components/contracts/WorkflowHistoryPanel' import { WorkflowHistoryPanel } from '@/components/contracts/WorkflowHistoryPanel'
import { PipelineTreePanel } from '@/components/pipeline/PipelineTreePanel'
import { PhaseBadge } from '@/components/PhaseBadge' import { PhaseBadge } from '@/components/PhaseBadge'
import { SlaTimer } from '@/components/SlaTimer' import { SlaTimer } from '@/components/SlaTimer'
import { EmptyState } from '@/components/EmptyState' import { EmptyState } from '@/components/EmptyState'
@ -338,12 +339,13 @@ export function InboxPage() {
{/* Panel 3 — Workflow + history */} {/* Panel 3 — Workflow + history */}
<aside className="hidden overflow-y-auto border-l border-slate-200 bg-white p-4 lg:block"> <aside className="hidden overflow-y-auto border-l border-slate-200 bg-white p-4 lg:block">
{!selectedId && ( {/* [S168 GĐ3-D — owner slot (63) chốt D] Chưa chọn HĐ ⇒ cây toàn trình (GĐ3)
<div className="rounded-lg border border-dashed border-slate-200 p-6 text-center text-sm text-slate-400"> thế chỗ ô trống; chọn HĐ thì nhường chỗ cho quy trình duyệt. Trade-off D
<X className="mx-auto mb-2 h-5 w-5" /> (cây rời panel khi chọn HĐ) — sub-implfe-gd3.md §1. */}
Quy trình duyệt sẽ hiện khi chọn . {/* [gate F-1/F-2] Panel-3 TỰ CUỘN ⇒ thân cây `lg:max-h-none` giữ đúng 1 thanh cuộn
</div> (chính /inbox là trang THIẾU ~26px vì dải stats + banner vai trò); sticky/self-start
)} của khuôn vô hiệu ở host display:block này — by-design. */}
{!selectedId && <PipelineTreePanel currentStage={3} bodyMaxHClass="max-h-[28rem] lg:max-h-none" />}
{selectedId && detail.data && <WorkflowHistoryPanel contract={detail.data} />} {selectedId && detail.data && <WorkflowHistoryPanel contract={detail.data} />}
</aside> </aside>
</div> </div>

View File

@ -7,9 +7,10 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import { useNavigate, useSearchParams } from 'react-router-dom' import { useNavigate, useSearchParams } from 'react-router-dom'
import { FileText, Plus, Search, X } from 'lucide-react' import { FileText, Plus, Search } from 'lucide-react'
import { ContractDetailContent } from '@/components/contracts/ContractDetailContent' import { ContractDetailContent } from '@/components/contracts/ContractDetailContent'
import { WorkflowHistoryPanel } from '@/components/contracts/WorkflowHistoryPanel' import { WorkflowHistoryPanel } from '@/components/contracts/WorkflowHistoryPanel'
import { PipelineTreePanel } from '@/components/pipeline/PipelineTreePanel'
import { PhaseBadge } from '@/components/PhaseBadge' import { PhaseBadge } from '@/components/PhaseBadge'
import { SlaTimer } from '@/components/SlaTimer' import { SlaTimer } from '@/components/SlaTimer'
import { EmptyState } from '@/components/EmptyState' import { EmptyState } from '@/components/EmptyState'
@ -216,12 +217,12 @@ export function MyContractsPage() {
{/* Panel 3 — Workflow + history */} {/* Panel 3 — Workflow + history */}
<aside className="hidden overflow-y-auto border-l border-slate-200 bg-white p-4 lg:block"> <aside className="hidden overflow-y-auto border-l border-slate-200 bg-white p-4 lg:block">
{!selectedId && ( {/* [S168 GĐ3-D — owner slot (63) chốt D] Chưa chọn HĐ ⇒ cây toàn trình (GĐ3)
<div className="rounded-lg border border-dashed border-slate-200 p-6 text-center text-sm text-slate-400"> thế chỗ ô trống; chọn HĐ thì nhường chỗ cho quy trình duyệt. Trade-off D
<X className="mx-auto mb-2 h-5 w-5" /> (cây rời panel khi chọn HĐ) — sub-implfe-gd3.md §1. */}
Quy trình duyệt sẽ hiện khi chọn . {/* [gate F-1/F-2] Panel-3 TỰ CUỘN ⇒ thân cây `lg:max-h-none` giữ đúng 1 thanh cuộn;
</div> sticky/self-start của khuôn vô hiệu ở host display:block này — by-design. */}
)} {!selectedId && <PipelineTreePanel currentStage={3} bodyMaxHClass="max-h-[28rem] lg:max-h-none" />}
{selectedId && detail.data && <WorkflowHistoryPanel contract={detail.data} />} {selectedId && detail.data && <WorkflowHistoryPanel contract={detail.data} />}
</aside> </aside>
</div> </div>