From 6262ddb76b755ad47aeb4bb60a7ca3b7306303dc Mon Sep 17 00:00:00 2001 From: SeongHyun Kim Date: Wed, 25 Mar 2026 15:01:53 +0900 Subject: [PATCH] =?UTF-8?q?feat(pop-work-detail):=20combined-final=20?= =?UTF-8?q?=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=8B=9C=EC=95=88=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20=E2=80=94=20=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=ED=99=95=EC=A0=95?= =?UTF-8?q?=EB=90=9C=20combined-final.html=20=EC=8B=9C=EC=95=88=EC=9D=84?= =?UTF-8?q?=20=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20PopWorkDetailComponen?= =?UTF-8?q?t=20=EC=A0=84=EB=A9=B4=20=EB=A6=AC=EB=94=94=EC=9E=90=EC=9D=B8:?= =?UTF-8?q?=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD:=20-=20=ED=83=AD=20=EB=B0=94=20=E2=86=92=20?= =?UTF-8?q?=EC=A2=8C=EC=B8=A1=20=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94(w-52)?= =?UTF-8?q?=20+=20=EC=9A=B0=EC=B8=A1=20=EC=BD=98=ED=85=90=EC=B8=A0=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=20=EA=B5=AC=EC=A1=B0=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=20-=20=EB=AA=A8=EB=8B=AC=20=ED=97=A4=EB=8D=94:=20"?= =?UTF-8?q?=EC=9E=91=EC=97=85=20=EC=83=81=EC=84=B8"=20+=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=EC=A7=80=EC=8B=9C=EB=B2=88=ED=98=B8=20+=20=EB=8B=AB?= =?UTF-8?q?=EA=B8=B0(X)=20=EB=B2=84=ED=8A=BC=20-=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EB=B0=94:=20#1a1a2e=20=EB=8B=A4=ED=81=AC=20=EB=B0=B0=EA=B2=BD,?= =?UTF-8?q?=20gap-8=20=EA=B0=84=EA=B2=A9,=20label(white/40)=20+=20value(wh?= =?UTF-8?q?ite)=20-=20=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94:=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A6=88=EB=B3=84=20=EA=B7=B8=EB=A3=B9=20(=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20+=20=EC=83=81=ED=83=9C=EC=83=89=20+=20?= =?UTF-8?q?=EC=A7=84=ED=96=89=EB=A5=A0),=20=EC=8B=A4=EC=A0=81=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=ED=8F=AC=ED=95=A8=20-=20KPI=20=EC=B9=B4=EB=93=9C:?= =?UTF-8?q?=20=EC=BD=98=ED=85=90=EC=B8=A0=EC=99=80=20=ED=95=A8=EA=BB=98=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20(44px/800weight,=20tabular-nums,?= =?UTF-8?q?=20divider)=20-=20=ED=92=8B=ED=84=B0:=2048px=20=EB=86=92?= =?UTF-8?q?=EC=9D=B4=203=EB=B2=84=ED=8A=BC=20(border-2=20+=20rounded-xl)?= =?UTF-8?q?=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EA=B5=AC=EC=A1=B0:=20-=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94:=20overflow-y-auto=20(?= =?UTF-8?q?=EC=9E=90=EC=B2=B4=20=EC=8A=A4=ED=81=AC=EB=A1=A4,=20thin=20scro?= =?UTF-8?q?llbar)=20-=20=EC=BD=98=ED=85=90=EC=B8=A0:=20overflow-y-auto=20(?= =?UTF-8?q?KPI=20+=20=EC=B2=B4=ED=81=AC=EB=A6=AC=EC=8A=A4=ED=8A=B8=20+=20?= =?UTF-8?q?=EC=9D=B4=EB=A0=A5=20=ED=95=A8=EA=BB=98=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4)=20-=20=EC=A0=95=EB=B3=B4=EB=B0=94=20+=20=ED=92=8B?= =?UTF-8?q?=ED=84=B0:=20flex-shrink-0=20(=EA=B3=A0=EC=A0=95)=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EB=B3=80=EA=B2=BD:=20-=20DESIGN=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0:=20kpi.valueSize=2040=E2=86=9244,=20weight?= =?UTF-8?q?=20700=E2=86=92800,=20section.titleSize=2016=E2=86=9213=20-=20?= =?UTF-8?q?=EC=84=B9=EC=85=98=20=EC=A0=9C=EB=AA=A9:=20text-xs=20uppercase?= =?UTF-8?q?=20tracking-widest=20text-gray-400=20-=20=EC=8B=A4=EC=A0=81=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5:=20native=20input=20+=20rounded-xl,=202xl=20?= =?UTF-8?q?font,=20color-coded=20borders=20-=20=EB=B6=88=EB=9F=89=20?= =?UTF-8?q?=EC=9C=A0=ED=98=95:=20=EC=B9=B4=EB=93=9C=ED=98=95=20UI=20(round?= =?UTF-8?q?ed-xl=20border,=20severity=20badge)=20-=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=EC=9D=B4=EB=A0=A5:=20table=20=ED=98=95=ED=83=9C=20(=EC=B0=A8?= =?UTF-8?q?=EC=88=98/=EC=83=9D=EC=82=B0=EC=88=98=EB=9F=89/=EC=96=91?= =?UTF-8?q?=ED=92=88/=EB=B6=88=EB=9F=89/=EB=88=84=EC=A0=81/=EC=8B=9C?= =?UTF-8?q?=EA=B0=81)=20-=20GroupTimerHeader:=20px-8=20=EA=B0=84=EA=B2=A9,?= =?UTF-8?q?=20=EB=AF=B8=EB=8B=88=EB=A9=80=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=9C=A0=EC=A7=80:=20-=20step=20=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=20+=20list=20=EB=AA=A8=EB=93=9C=20=EB=AA=A8=EB=91=90?= =?UTF-8?q?=20=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=95=88=EC=97=90=EC=84=9C=20=EC=A0=95=EC=83=81=20=EB=8F=99?= =?UTF-8?q?=EC=9E=91=20-=20=EC=B2=B4=ED=81=AC=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=9E=85=EB=A0=A5/=EC=A0=80=EC=9E=A5,=20=EA=B7=B8=EB=A3=B9?= =?UTF-8?q?=20=ED=83=80=EC=9D=B4=EB=A8=B8,=20=EC=8B=A4=EC=A0=81=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D/=EC=9D=B4=EB=A0=A5=20=EB=AA=A8=EB=91=90=20?= =?UTF-8?q?=EB=B3=B4=EC=A1=B4=20-=202=EB=8B=A8=EA=B3=84=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20(=EC=9E=91=EC=97=85=EC=99=84=EB=A3=8C),=20=EB=B6=88?= =?UTF-8?q?=EB=9F=89=EB=93=B1=EB=A1=9D=20=EC=97=B0=EB=8F=99=20=EC=9C=A0?= =?UTF-8?q?=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PopWorkDetailComponent.tsx | 1354 +++++++++-------- 1 file changed, 731 insertions(+), 623 deletions(-) diff --git a/frontend/lib/registry/pop-components/pop-work-detail/PopWorkDetailComponent.tsx b/frontend/lib/registry/pop-components/pop-work-detail/PopWorkDetailComponent.tsx index 2d199537..30fe9a7f 100644 --- a/frontend/lib/registry/pop-components/pop-work-detail/PopWorkDetailComponent.tsx +++ b/frontend/lib/registry/pop-components/pop-work-detail/PopWorkDetailComponent.tsx @@ -134,22 +134,23 @@ const DEFAULT_CFG: PopWorkDetailConfig = { }; // ======================================== -// ISA-101 디자인 토큰 (산업 터치 기준) +// ISA-101 디자인 토큰 (combined-final 기준) // ======================================== const DESIGN = { button: { height: 56, minWidth: 100 }, input: { height: 56 }, stat: { valueSize: 40, labelSize: 14, weight: 700 }, - section: { titleSize: 16, gap: 20 }, + section: { titleSize: 13, gap: 20 }, tab: { height: 48 }, footer: { height: 64 }, - header: { height: 56 }, - kpi: { valueSize: 40, labelSize: 14, weight: 700 }, + header: { height: 48 }, + kpi: { valueSize: 44, labelSize: 13, weight: 800 }, nav: { height: 56 }, - infoBar: { labelSize: 14, valueSize: 16 }, - defectRow: { height: 56 }, - bg: { page: '#F5F5F5', card: '#FFFFFF', header: '#263238' }, + infoBar: { labelSize: 12, valueSize: 14 }, + defectRow: { height: 44 }, + sidebar: { width: 208 }, + bg: { page: '#F5F5F5', card: '#FFFFFF', header: '#1a1a2e', infoBar: '#1a1a2e' }, } as const; const COLORS = { @@ -707,6 +708,30 @@ export function PopWorkDetailComponent({ await handleTimerAction("complete"); }, [handleTimerAction]); + // 현재 탭의 그룹 목록 + const currentTabGroups = useMemo( + () => (activePhaseTab && !resultTabActive ? groupsByPhase[activePhaseTab] ?? [] : []), + [activePhaseTab, resultTabActive, groupsByPhase] + ); + + // 탭 전환 시 해당 phase의 첫 번째 그룹 자동 선택 + const handlePhaseTabChange = useCallback((phase: string) => { + setActivePhaseTab(phase); + setResultTabActive(false); + const phaseGrps = groupsByPhase[phase]; + if (phaseGrps && phaseGrps.length > 0) { + setSelectedGroupId(phaseGrps[0].itemId); + } + contentRef.current?.scrollTo({ top: 0, behavior: "smooth" }); + }, [groupsByPhase]); + + const handleResultTabClick = useCallback(() => { + setResultTabActive(true); + setActivePhaseTab(null); + setSelectedGroupId(null); + contentRef.current?.scrollTo({ top: 0, behavior: "smooth" }); + }, []); + // ======================================== // 안전 장치 // ======================================== @@ -749,37 +774,32 @@ export function PopWorkDetailComponent({ } const selectedGroup = groups.find((g) => g.itemId === selectedGroupId); - // 현재 탭의 그룹 목록 - const currentTabGroups = useMemo( - () => (activePhaseTab && !resultTabActive ? groupsByPhase[activePhaseTab] ?? [] : []), - [activePhaseTab, resultTabActive, groupsByPhase] - ); - - // 탭 전환 시 해당 phase의 첫 번째 그룹 자동 선택 - const handlePhaseTabChange = useCallback((phase: string) => { - setActivePhaseTab(phase); - setResultTabActive(false); - const phaseGrps = groupsByPhase[phase]; - if (phaseGrps && phaseGrps.length > 0) { - setSelectedGroupId(phaseGrps[0].itemId); - } - contentRef.current?.scrollTo({ top: 0, behavior: "smooth" }); - }, [groupsByPhase]); - - const handleResultTabClick = useCallback(() => { - setResultTabActive(true); - setActivePhaseTab(null); - setSelectedGroupId(null); - contentRef.current?.scrollTo({ top: 0, behavior: "smooth" }); - }, []); - // ======================================== - // 렌더링 + // 렌더링 (combined-final 레이아웃) // ======================================== + const woNo = parentRow?.wo_no ? String(parentRow.wo_no) : ""; + return ( -
- {/* ── 고정 헤더: 작업 정보 ── */} +
+ {/* ── 모달 헤더: 미니멀 ── */} +
+
+

작업 상세

+ {woNo && {woNo}} +
+ +
+ + {/* ── 정보바: 미니멀 다크 (고정) ── */} {cfg.infoBar.enabled && ( 0 ? cfg.infoBar.fields : DEFAULT_INFO_FIELDS} @@ -788,299 +808,360 @@ export function PopWorkDetailComponent({ /> )} - {/* ── KPI 카드 (항상 표시) ── */} - + {/* ── 본문: 사이드바 + 콘텐츠 ── */} +
- {/* ── 탭 바 ── */} -
- {availablePhases.map((phase) => { - const progress = phaseProgress[phase]; - const isActive = !resultTabActive && activePhaseTab === phase; - return ( - - ); - })} - {hasResultSections && ( - - )} -
+ {/* ===== 사이드바 ===== */} +
+
+ {/* 페이즈별 그룹 */} + {availablePhases.map((phase) => { + const phaseGrps = groupsByPhase[phase] || []; + const progress = phaseProgress[phase]; + const allDone = progress && progress.done >= progress.total && progress.total > 0; + const anyActive = phaseGrps.some((g) => g.stepStatus === "active"); - {/* ── 콘텐츠 영역 (스크롤) ── */} -
- {/* 실적 입력 패널 (hidden으로 상태 유지, unmount 방지) */} - {hasResultSections && ( -
- { - setProcessData((prev) => prev ? { ...prev, ...updated } : prev); - publish("process_completed", { workOrderProcessId, status: updated?.status }); - }} - /> -
- )} - - {/* 체크리스트 영역 */} -
- {cfg.displayMode === "step" ? ( - /* ======== 스텝 모드 ======== */ - <> - {showQuantityPanel || allItemsCompleted ? ( - /* 수량 등록 + 공정 완료 화면 */ -
- -

모든 작업 항목이 완료되었습니다

- - {cfg.showQuantityInput && !isProcessCompleted && !hasResultSections && ( -
-

실적 수량 등록

-
-
- 양품 - setGoodQty(e.target.value)} placeholder="0" /> -
-
- 불량 - setDefectQty(e.target.value)} placeholder="0" /> -
- {(parseInt(goodQty, 10) || 0) + (parseInt(defectQty, 10) || 0) > 0 && ( -

- 합계: {(parseInt(goodQty, 10) || 0) + (parseInt(defectQty, 10) || 0)} -

- )} -
+ return ( +
+ {/* 페이즈 헤더 */} +
+
+ {allDone ? ( + + ) : anyActive ? ( + + ) : ( + + )}
- )} + + {cfg.phaseLabels[phase] ?? phase} + + + {progress?.done ?? 0}/{progress?.total ?? 0} + +
- {isProcessCompleted && ( - - 공정이 완료되었습니다 - - )} + {/* 그룹 항목 */} +
+ {phaseGrps.map((g) => { + const isSelected = selectedGroupId === g.itemId && !resultTabActive; + return ( + + ); + })} +
- ) : ( - /* 단계별 항목 표시 */ - <> - {/* 그룹 헤더 + 타이머 + 진행률 */} - {selectedGroup && ( - <> - -
-
- {currentItemIdx + 1} / {currentItems.length} + ); + })} + + {/* 실적 그룹 */} + {hasResultSections && ( +
+
+
+ +
+ 실적 + + {isProcessCompleted ? "확정" : "미확정"} + +
+
+ +
+
+ )} +
+
+ + {/* ===== 메인 콘텐츠 ===== */} +
+ {/* 실적 입력 패널 (hidden으로 상태 유지) */} + {hasResultSections && ( +
+ { + setProcessData((prev) => prev ? { ...prev, ...updated } : prev); + publish("process_completed", { workOrderProcessId, status: updated?.status }); + }} + /> +
+ )} + + {/* 체크리스트 영역 */} +
+ {cfg.displayMode === "step" ? ( + /* ======== 스텝 모드 ======== */ + <> + {showQuantityPanel || allItemsCompleted ? ( +
+ +

모든 작업 항목이 완료되었습니다

+ {cfg.showQuantityInput && !isProcessCompleted && !hasResultSections && ( +
+

실적 수량 등록

+
+
+ 양품 + setGoodQty(e.target.value)} placeholder="0" /> +
+
+ 불량 + setDefectQty(e.target.value)} placeholder="0" /> +
+ {(parseInt(goodQty, 10) || 0) + (parseInt(defectQty, 10) || 0) > 0 && ( +

+ 합계: {(parseInt(goodQty, 10) || 0) + (parseInt(defectQty, 10) || 0)} +

+ )}
-
-
0 && selectedGroup.total > 0 ? ((selectedGroup.completed / selectedGroup.total) * 100) : 0}%`, - }} +
+ )} + {isProcessCompleted && ( + + 공정이 완료되었습니다 + + )} +
+ ) : ( + <> + {/* 그룹 헤더 + 타이머 + 진행률 */} + {selectedGroup && ( + <> + +
+
+ {currentItemIdx + 1} / {currentItems.length} +
+
+
0 && selectedGroup.total > 0 ? ((selectedGroup.completed / selectedGroup.total) * 100) : 0}%`, + }} + /> +
+
+ + )} + + {/* 현재 항목 1개 표시 */} +
+ {currentItems[currentItemIdx] && ( +
+ {currentItems[currentItemIdx].started_at && ( +
+ + {currentItems[currentItemIdx].recorded_at + ? formatDuration(currentItems[currentItemIdx].started_at!, currentItems[currentItemIdx].recorded_at!) + : "진행 중..."} +
+ )} +
-
- - )} + )} +
- {/* 현재 항목 1개 표시 */} -
- {currentItems[currentItemIdx] && ( -
- {currentItems[currentItemIdx].started_at && ( -
- - {currentItems[currentItemIdx].recorded_at - ? formatDuration(currentItems[currentItemIdx].started_at!, currentItems[currentItemIdx].recorded_at!) - : "진행 중..."} -
+ {/* 스텝 네비게이션 */} +
+ + + + {selectedGroup?.title} ({currentItemIdx + 1}/{currentItems.length}) + + +
+ + )} + + ) : ( + /* ======== 리스트 모드 ======== */ +
+ {/* KPI 카드 (콘텐츠와 함께 스크롤) */} + + + {/* 그룹 헤더 + 타이머 */} + {selectedGroup && ( + + )} + + {/* 체크리스트 콘텐츠 */} +
+ {selectedGroupId && ( +
+ {currentItems.map((item) => ( + -
- )} -
- - {/* 스텝 네비게이션 */} -
- - - - {selectedGroup?.title} ({currentItemIdx + 1}/{currentItems.length}) - - - -
- - )} - - ) : ( - /* ======== 리스트 모드 ======== */ - <> - {/* 탭 내 그룹 목록 표시 (그룹이 여러 개인 경우) */} - {currentTabGroups.length > 1 && ( -
- {currentTabGroups.map((g) => ( - - ))} + ))} +
+ )}
- )} - - {/* 그룹 헤더 + 타이머 */} - {selectedGroup && ( - - )} - - {/* 체크리스트 콘텐츠 */} -
- {selectedGroupId && ( -
- {currentItems.map((item) => ( - - ))} -
- )}
- - )} + )} +
{/* ── 고정 풋터 액션바 ── */} {!isProcessCompleted && (
{/* 일시정지 */} - + {/* 불량등록 */} - + {/* 작업완료 (2단계 확인) */} {!confirmCompleteOpen ? ( - + ) : (
- - +
)}
@@ -1151,10 +1227,10 @@ export function PopWorkDetailComponent({ {isProcessCompleted && (
- + 공정이 완료되었습니다 @@ -1390,274 +1466,277 @@ function ResultPanel({ }; return ( -
-
+
+ {/* KPI 카드 (실적 탭에서도 표시) */} + + +
{/* 확정 상태 배너 */} {isConfirmed && ( -
- - 실적이 확정되었습니다 +
+ + 실적이 확정되었습니다
)} - {/* 공정 현황: 접수량 / 작업완료 / 잔여 + 앞공정 완료량 */} -
-
공정 현황
-
-
-
{inputQty}
-
접수량
-
-
-
{accumulatedTotal}
-
작업완료
-
-
-
0 ? COLORS.warning : COLORS.good} style={{ fontSize: `${DESIGN.stat.valueSize}px`, fontWeight: DESIGN.stat.weight }}> - {remainingQty} -
-
잔여
-
- {availableInfo && availableInfo.availableQty > 0 && ( -
-
{availableInfo.availableQty}
-
추가접수가능
-
- )} -
- {inputQty > 0 && ( -
-
-
- )} - {availableInfo && ( -
- 앞공정 완료: {availableInfo.prevGoodQty} - 지시수량: {availableInfo.instructionQty} -
- )} -
- - {/* 누적 실적 현황 */} -
-
누적 실적
-
-
-
{accumulatedTotal}
-
총생산
-
-
-
{accumulatedGood}
-
양품
-
-
-
{accumulatedDefect}
-
불량
-
-
-
{history.length}
-
차수
-
-
- {accumulatedTotal > 0 && ( -
-
0 ? (accumulatedGood / accumulatedTotal) * 100 : 0}%` }} - /> -
- )} -
- {/* 이번 차수 실적 입력 */} {!isConfirmed && ( -
-
이번 차수 실적
+
+
이번 차수 실적 입력
{/* 생산수량 */} {enabledSections.some((s) => s.type === "total-qty") && ( -
- -
- setBatchQty(e.target.value)} - placeholder="0" - /> - EA -
-
- )} - - {/* 양품/불량 */} - {enabledSections.some((s) => s.type === "good-defect") && ( -
- -
+
+
+
- 양품 - 0 ? String(batchGood) : ""} - readOnly - placeholder="자동" - /> -
-
- 불량 - setBatchDefect(e.target.value)} + className="h-14 w-36 rounded-xl border-2 border-gray-200 px-4 text-center text-2xl font-bold text-gray-900 transition-colors focus:border-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-100" + value={batchQty} + onChange={(e) => setBatchQty(e.target.value)} placeholder="0" /> + EA
- {(parseInt(batchQty, 10) || 0) > 0 && ( -

- 양품 {batchGood} = 생산 {batchQty} - 불량 {batchDefect || 0} -

- )} -
- )} - - {/* 불량 유형 상세 */} - {enabledSections.some((s) => s.type === "defect-types") && ( -
-
- - -
- {defectEntries.length === 0 ? ( -

등록된 불량 유형이 없습니다.

- ) : ( -
- {defectEntries.map((entry, idx) => ( -
- - updateDefectEntry(idx, "qty", e.target.value)} - placeholder="수량" - /> - - + {enabledSections.some((s) => s.type === "good-defect") && ( + <> +
+ + 0 ? String(batchGood) : ""} + readOnly + /> +
+
+ + setBatchDefect(e.target.value)} + placeholder="0" + /> +
+ {(parseInt(batchQty, 10) || 0) > 0 && ( +
+

양품 {batchGood} = 생산 {batchQty} - 불량 {batchDefect || 0}

- ))} -
+ )} + )}
)} - - {/* 비고 */} - {enabledSections.some((s) => s.type === "note") && ( -
- -