로그인 전까지 기능
This commit is contained in:
22
client/src/components/AnswerPanel.tsx
Normal file
22
client/src/components/AnswerPanel.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
type Props = {
|
||||
suggestions: string[]
|
||||
}
|
||||
|
||||
export default function AnswerPanel({ suggestions }: Props) {
|
||||
return (
|
||||
<div className="panel answer-panel">
|
||||
<div className="panel-title">대답</div>
|
||||
<div className="answer-content">
|
||||
{suggestions.length === 0 ? (
|
||||
<div className="placeholder">질문이 감지되면 답변 후보가 표시됩니다.</div>
|
||||
) : (
|
||||
<ul className="answer-list">
|
||||
{suggestions.map((item, idx) => (
|
||||
<li key={`${item}-${idx}`}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
60
client/src/components/MeetingList.tsx
Normal file
60
client/src/components/MeetingList.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { MeetingSummary } from '../lib/api'
|
||||
|
||||
type Props = {
|
||||
meetings: MeetingSummary[]
|
||||
isEditMode: boolean
|
||||
selectedIds: Set<number>
|
||||
onToggleSelect: (id: number) => void
|
||||
onSelectMeeting: (id: number) => void
|
||||
}
|
||||
|
||||
function formatDate(value: string) {
|
||||
const date = new Date(value)
|
||||
return date.toLocaleString('ko-KR', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
})
|
||||
}
|
||||
|
||||
export default function MeetingList({
|
||||
meetings,
|
||||
isEditMode,
|
||||
selectedIds,
|
||||
onToggleSelect,
|
||||
onSelectMeeting,
|
||||
}: Props) {
|
||||
return (
|
||||
<div className="meeting-list">
|
||||
{meetings.length === 0 && (
|
||||
<div className="placeholder">저장된 대화가 없습니다.</div>
|
||||
)}
|
||||
{meetings.map((meeting) => (
|
||||
<div key={meeting.id} className="meeting-item">
|
||||
{isEditMode ? (
|
||||
<label className="meeting-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedIds.has(meeting.id)}
|
||||
onChange={() => onToggleSelect(meeting.id)}
|
||||
/>
|
||||
<span>{formatDate(meeting.started_at)}</span>
|
||||
</label>
|
||||
) : (
|
||||
<button
|
||||
className="meeting-button"
|
||||
type="button"
|
||||
onClick={() => onSelectMeeting(meeting.id)}
|
||||
>
|
||||
<span className="bullet">•</span>
|
||||
<span>{formatDate(meeting.started_at)}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
32
client/src/components/TranscriptPanel.tsx
Normal file
32
client/src/components/TranscriptPanel.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
type TranscriptLine = {
|
||||
id: number
|
||||
ts: string
|
||||
text: string
|
||||
isFinal: boolean
|
||||
}
|
||||
|
||||
type Props = {
|
||||
transcriptLines: TranscriptLine[]
|
||||
}
|
||||
|
||||
export default function TranscriptPanel({ transcriptLines }: Props) {
|
||||
return (
|
||||
<div className="panel transcript-panel">
|
||||
<div className="panel-title">대화/STT</div>
|
||||
<div className="transcript-content">
|
||||
{transcriptLines.length === 0 && (
|
||||
<div className="placeholder">대화를 시작하면 STT 로그가 표시됩니다.</div>
|
||||
)}
|
||||
{transcriptLines.map((line) => (
|
||||
<div
|
||||
key={line.id}
|
||||
className={`transcript-line ${line.isFinal ? '' : 'live'}`.trim()}
|
||||
>
|
||||
<span className="transcript-time">-</span>
|
||||
<span className="transcript-text">{line.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user