feat: 회의 목록 반응형 그리드 레이아웃 + ellipsis UX 개선
단일 열 리스트 → CSS Grid (1~4열 반응형). 제목/메타 text-overflow: ellipsis, hover translateY 효과, 상태 LED+날짜 상단 배치, d3roTypo 토큰 사용.
This commit is contained in:
parent
6dd4846de7
commit
588147280e
1 changed files with 65 additions and 20 deletions
|
|
@ -238,42 +238,87 @@ export function MeetingModePage(): React.ReactElement {
|
||||||
) : sessions.length === 0 ? (
|
) : sessions.length === 0 ? (
|
||||||
<EmptyStateCard message={`${t('meeting.noSessions')}\n${t('meeting.noSessionsDesc')}`} />
|
<EmptyStateCard message={`${t('meeting.noSessions')}\n${t('meeting.noSessionsDesc')}`} />
|
||||||
) : (
|
) : (
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: {
|
||||||
|
xs: '1fr',
|
||||||
|
sm: 'repeat(2, 1fr)',
|
||||||
|
md: 'repeat(3, 1fr)',
|
||||||
|
lg: 'repeat(4, 1fr)',
|
||||||
|
},
|
||||||
|
gap: 1.5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{sessions.map((session) => (
|
{sessions.map((session) => (
|
||||||
<Box
|
<Box
|
||||||
key={session.id}
|
key={session.id}
|
||||||
onClick={() => handleViewDetail(session.id)}
|
onClick={() => handleViewDetail(session.id)}
|
||||||
sx={{ cursor: 'pointer', '&:hover': { opacity: 0.85 } }}
|
sx={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
transition: 'transform 0.15s ease, box-shadow 0.15s ease',
|
||||||
|
'&:hover': { transform: 'translateY(-2px)' },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<MetalCard>
|
<MetalCard>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
{/* 상태 LED + 날짜 */}
|
||||||
<Box>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1 }}>
|
||||||
<PhosphorText variant="label">
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}>
|
||||||
|
<Led color={statusColor(session.status)} size={6} />
|
||||||
|
<PhosphorText variant="dim" sx={{ fontSize: d3roTypo.meta.size }}>
|
||||||
|
{session.status === 'completed' ? t('meeting.completed')
|
||||||
|
: session.status === 'error' ? t('meeting.error')
|
||||||
|
: session.status === 'processing' ? t('meeting.processing')
|
||||||
|
: t('meeting.recording')}
|
||||||
|
</PhosphorText>
|
||||||
|
</Box>
|
||||||
|
<PhosphorText variant="dim" sx={{ fontSize: d3roTypo.meta.size }}>
|
||||||
|
{formatDate(session.startedAt)}
|
||||||
|
</PhosphorText>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 제목 — ellipsis */}
|
||||||
|
<PhosphorText
|
||||||
|
variant="compact"
|
||||||
|
sx={{
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
display: 'block',
|
||||||
|
mb: 0.5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{session.title ?? t('meeting.untitled')}
|
{session.title ?? t('meeting.untitled')}
|
||||||
</PhosphorText>
|
</PhosphorText>
|
||||||
<PhosphorText variant="dim" sx={{ mt: 0.5 }}>
|
|
||||||
|
{/* 메타 — 소요 시간 · 메모 수 */}
|
||||||
|
<PhosphorText variant="dim" sx={{
|
||||||
|
fontSize: d3roTypo.meta.size,
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
}}>
|
||||||
{session.durationMs != null
|
{session.durationMs != null
|
||||||
? t('meeting.duration', { minutes: Math.round(session.durationMs / 60000) })
|
? t('meeting.duration', { minutes: Math.round(session.durationMs / 60000) })
|
||||||
: '—'}
|
: '—'}
|
||||||
{' · '}
|
{' · '}
|
||||||
{t('meeting.memos')}: {session.memoCount}
|
{t('meeting.memos')}: {session.memoCount}
|
||||||
</PhosphorText>
|
</PhosphorText>
|
||||||
</Box>
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
||||||
<PhosphorText variant="dim">{formatDate(session.startedAt)}</PhosphorText>
|
|
||||||
<Led color={statusColor(session.status)} size={8} />
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* 프로세싱 중인 세션 표시 */}
|
{/* 프로세싱 진행 바 */}
|
||||||
{session.status === 'processing' && progress && progress.sessionId === session.id && (
|
{session.status === 'processing' && progress && progress.sessionId === session.id && (
|
||||||
<Box sx={{ mt: 1 }}>
|
<Box sx={{ mt: 1 }}>
|
||||||
<LinearProgress
|
<LinearProgress
|
||||||
variant="determinate"
|
variant="determinate"
|
||||||
value={progress.percent}
|
value={progress.percent}
|
||||||
sx={{ height: 4, borderRadius: 2 }}
|
sx={{
|
||||||
|
height: 3,
|
||||||
|
borderRadius: 2,
|
||||||
|
bgcolor: d3roPalette.bg.inset,
|
||||||
|
'& .MuiLinearProgress-bar': { bgcolor: d3roPalette.accent.amber },
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<PhosphorText variant="dim" sx={{ mt: 0.5, fontSize: 11 }}>
|
<PhosphorText variant="dim" sx={{ mt: 0.5, fontSize: d3roTypo.nano.size }}>
|
||||||
{t(`meeting.processingStep.${progress.step}` as Parameters<typeof t>[0])}
|
{t(`meeting.processingStep.${progress.step}` as Parameters<typeof t>[0])}
|
||||||
</PhosphorText>
|
</PhosphorText>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue