fix: /simplify 리뷰 — UX/코드 품질 10건 수정
- EditableSegment: readOnly prop 추가, hover 배경색 효과 - MeetingDetailTabs: useMemo(parseSegments), 이중 debounce 제거, cleanup - MeetingChatPanel: sessionId deps, 에러 표시, key=timestamp - ExportMenu: i18n 위반 수정 (하드코딩 → t()) - MeetingModePage: 에러 Snackbar 피드백 (startRecording, viewDetail) - TranscriptTab: polish 실패 피드백 - 12개 locale i18n 키 5개 추가
This commit is contained in:
parent
403985e2cd
commit
f0e221058d
18 changed files with 179 additions and 49 deletions
|
|
@ -1,7 +1,7 @@
|
|||
// src/renderer/components/meeting/MeetingDetailTabs.tsx
|
||||
// Phase 14.5: 풀스크린 탭 전환 컨테이너 — 전사 + 동적 문서 탭
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react'
|
||||
import {
|
||||
Box,
|
||||
Tabs,
|
||||
|
|
@ -33,8 +33,6 @@ interface MeetingDetailTabsProps {
|
|||
onBack: () => void
|
||||
}
|
||||
|
||||
const AUTOSAVE_DELAY = 500
|
||||
|
||||
// 세그먼트 파싱: rawTranscript를 줄 단위로 분리
|
||||
function parseSegments(
|
||||
rawTranscript: string | null,
|
||||
|
|
@ -78,17 +76,19 @@ export function MeetingDetailTabs({
|
|||
}: MeetingDetailTabsProps): React.ReactElement {
|
||||
const { t } = useI18n()
|
||||
const [detail, setDetail] = useState<MeetingSessionDetail>(initialDetail)
|
||||
const [documents, setDocuments] = useState<MeetingDocument[]>(initialDetail.documents ?? [])
|
||||
const [documents, setDocuments] = useState<MeetingDocument[]>((initialDetail.documents ?? []).filter(Boolean))
|
||||
const [templates, setTemplates] = useState<MeetingDocTemplate[]>([])
|
||||
const [tabIndex, setTabIndex] = useState(0)
|
||||
const [dialogOpen, setDialogOpen] = useState(false)
|
||||
const [generating, setGenerating] = useState(false)
|
||||
const [editingTitle, setEditingTitle] = useState(false)
|
||||
const [titleDraft, setTitleDraft] = useState(detail.title ?? '')
|
||||
const autoSaveTimers = useRef<Record<string, ReturnType<typeof setTimeout>>>({})
|
||||
|
||||
// 세그먼트 파싱 (rawTranscript 기반)
|
||||
const segments = parseSegments(detail.rawTranscript, detail.editedTranscript)
|
||||
const segments = useMemo(
|
||||
() => parseSegments(detail.rawTranscript, detail.editedTranscript),
|
||||
[detail.rawTranscript, detail.editedTranscript],
|
||||
)
|
||||
|
||||
// 템플릿 로드
|
||||
useEffect(() => {
|
||||
|
|
@ -134,19 +134,16 @@ export function MeetingDetailTabs({
|
|||
[detail.id],
|
||||
)
|
||||
|
||||
// ── 문서 내용 자동저장 ──
|
||||
// ── 문서 내용 자동저장 (DocumentTab 내부에 debounce 있음, 이중 래핑 제거) ──
|
||||
const handleDocContentChange = useCallback(
|
||||
(docId: string, content: string) => {
|
||||
if (autoSaveTimers.current[docId]) clearTimeout(autoSaveTimers.current[docId])
|
||||
autoSaveTimers.current[docId] = setTimeout(async () => {
|
||||
await window.electronAPI.meetingMode.updateDocument({
|
||||
documentId: docId,
|
||||
content,
|
||||
})
|
||||
setDocuments((prev) =>
|
||||
prev.map((d) => (d.id === docId ? { ...d, content } : d)),
|
||||
)
|
||||
}, AUTOSAVE_DELAY)
|
||||
async (docId: string, content: string) => {
|
||||
await window.electronAPI.meetingMode.updateDocument({
|
||||
documentId: docId,
|
||||
content,
|
||||
})
|
||||
setDocuments((prev) =>
|
||||
prev.map((d) => (d.id === docId ? { ...d, content } : d)),
|
||||
)
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
|
@ -286,8 +283,8 @@ export function MeetingDetailTabs({
|
|||
}}
|
||||
>
|
||||
<Tab label={t('meeting.transcriptTab')} />
|
||||
{documents.map((doc) => (
|
||||
<Tab key={doc.id} label={doc.title} />
|
||||
{documents.filter(Boolean).map((doc) => (
|
||||
<Tab key={doc.id} label={doc.title ?? t('meeting.untitled')} />
|
||||
))}
|
||||
<Tab
|
||||
icon={<AddIcon sx={{ fontSize: 16 }} />}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue