feat: add LMS web client
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { Terminal } from 'lucide-react'
|
||||
|
||||
type ModuleLogEntry = {
|
||||
time: string
|
||||
level: 'INFO' | 'WARN' | 'ERROR'
|
||||
source: string
|
||||
message: string
|
||||
}
|
||||
|
||||
type Props = {
|
||||
dark: boolean
|
||||
borderSoft: string
|
||||
textMid: string
|
||||
selectedModuleState: 'running' | 'stopped' | 'uninstalled' | null
|
||||
moduleLogsLoading: boolean
|
||||
moduleLogMessage: string | null
|
||||
visibleModuleLogEntries: ModuleLogEntry[]
|
||||
moduleLogScrollRef: React.RefObject<HTMLDivElement | null>
|
||||
onScroll: () => void
|
||||
}
|
||||
|
||||
function WorkspaceLogView({
|
||||
dark,
|
||||
borderSoft,
|
||||
textMid,
|
||||
selectedModuleState,
|
||||
moduleLogsLoading,
|
||||
moduleLogMessage,
|
||||
visibleModuleLogEntries,
|
||||
moduleLogScrollRef,
|
||||
onScroll
|
||||
}: Props): React.JSX.Element {
|
||||
return (
|
||||
<section
|
||||
className={['min-w-0 overflow-hidden', dark ? 'bg-[#1f1f1f]' : 'bg-[#f7f8fb]'].join(' ')}
|
||||
style={{ gridColumn: '4 / span 3', gridRow: 2 }}
|
||||
>
|
||||
<div className={['flex h-full min-h-0 flex-col', dark ? 'bg-[#1f1f1f]' : 'bg-[#f7f8fb]'].join(' ')}>
|
||||
<div className={['flex h-11 items-center justify-between border-b px-4', borderSoft].join(' ')}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Terminal size={15} className={dark ? 'text-white/58' : 'text-[#687284]'} />
|
||||
<span className={['text-[12px] font-bold', textMid].join(' ')}>실시간 로그</span>
|
||||
</div>
|
||||
<span className={['rounded px-2 py-1 font-mono text-[10px]', dark ? 'bg-white/7 text-white/45' : 'bg-[#e8ebf2] text-[#687284]'].join(' ')}>
|
||||
{selectedModuleState === 'running' ? 'STREAMING' : 'IDLE'}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
ref={moduleLogScrollRef}
|
||||
onScroll={onScroll}
|
||||
className={['thin-y-scrollbar min-h-0 flex-1 overflow-auto p-4 font-mono text-[12px] leading-5', dark ? 'bg-[#171717] text-white/68' : 'bg-[#f9fafc] text-[#374151]'].join(' ')}
|
||||
>
|
||||
{moduleLogsLoading ? (
|
||||
<div className={['grid h-full place-items-center font-sans text-[12px] font-semibold', dark ? 'text-white/42' : 'text-[#7b8494]'].join(' ')}>
|
||||
로그 불러오는 중
|
||||
</div>
|
||||
) : visibleModuleLogEntries.length === 0 ? (
|
||||
<div className={['grid h-full place-items-center font-sans text-[12px] font-semibold', dark ? 'text-white/42' : 'text-[#7b8494]'].join(' ')}>
|
||||
{moduleLogMessage ?? '표시할 로그 없음'}
|
||||
</div>
|
||||
) : visibleModuleLogEntries.map((entry, index) => (
|
||||
<div key={`${entry.time}-${entry.source}-${index}`} className="flex min-w-0 gap-1.5">
|
||||
<span className={dark ? 'w-[52px] shrink-0 text-white/28' : 'w-[52px] shrink-0 text-[#9aa3b2]'}>{entry.time}</span>
|
||||
<span
|
||||
className={[
|
||||
'w-[44px] shrink-0',
|
||||
entry.level === 'ERROR'
|
||||
? 'text-[#ef4444]'
|
||||
: entry.level === 'WARN'
|
||||
? 'text-[#f59e0b]'
|
||||
: dark ? 'text-[#5eead4]' : 'text-[#0f766e]'
|
||||
].join(' ')}
|
||||
>
|
||||
[{entry.level}]
|
||||
</span>
|
||||
<span className={['min-w-0 flex-1 whitespace-pre-wrap break-words', dark ? 'text-white/72' : 'text-[#2f3747]'].join(' ')}>
|
||||
{entry.message}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkspaceLogView
|
||||
Reference in New Issue
Block a user