Highly ambitious ATProtocol AppView service and sdks
138
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 99 lines 3.5 kB view raw
1import { LogLevelBadge } from "./LogLevelBadge.tsx"; 2import { Text } from "./Text.tsx"; 3import { Card } from "./Card.tsx"; 4import { NetworkSlicesSliceGetJobLogsLogEntry } from "../../client.ts"; 5import { formatTimestamp } from "../../utils/time.ts"; 6 7interface LogViewerProps { 8 logs: NetworkSlicesSliceGetJobLogsLogEntry[]; 9 emptyMessage?: string; 10} 11 12export function LogViewer({ 13 logs, 14 emptyMessage = "No logs available.", 15}: LogViewerProps) { 16 if (logs.length === 0) { 17 return ( 18 <div className="p-8 text-center"> 19 <Text as="p" variant="muted"> 20 {emptyMessage} 21 </Text> 22 </div> 23 ); 24 } 25 26 const errorCount = logs.filter((l) => l.level === "error").length; 27 const warnCount = logs.filter((l) => l.level === "warn").length; 28 const infoCount = logs.filter((l) => l.level === "info").length; 29 30 return ( 31 <> 32 <Card> 33 <div className="bg-zinc-50 dark:bg-zinc-800 px-6 py-3 border-b border-zinc-200 dark:border-zinc-700 rounded-t-sm"> 34 <div className="flex items-center gap-4"> 35 <Text as="span" size="sm"> 36 Total: <strong>{logs.length}</strong> 37 </Text> 38 {errorCount > 0 && ( 39 <Text as="span" size="sm" variant="error"> 40 Errors: <strong>{errorCount}</strong> 41 </Text> 42 )} 43 {warnCount > 0 && ( 44 <Text as="span" size="sm" variant="warning"> 45 Warnings: <strong>{warnCount}</strong> 46 </Text> 47 )} 48 <Text 49 as="span" 50 size="sm" 51 className="text-blue-600 dark:text-blue-400" 52 > 53 Info: <strong>{infoCount}</strong> 54 </Text> 55 </div> 56 </div> 57 58 <Card.Content className="divide-y divide-zinc-200 dark:divide-zinc-700"> 59 {logs.map((log) => ( 60 <div 61 key={log.id} 62 className="p-3 hover:bg-zinc-50 dark:hover:bg-zinc-800 font-mono text-sm" 63 > 64 <div className="flex items-start gap-3"> 65 <span className="text-xs text-zinc-500 dark:text-zinc-400"> 66 {formatTimestamp(log.createdAt)} 67 </span> 68 <LogLevelBadge level={log.level} /> 69 <div className="flex-1"> 70 <Text as="div" size="sm"> 71 {log.message} 72 </Text> 73 {log.metadata && Object.keys(log.metadata).length > 0 && ( 74 <details className="mt-2"> 75 <summary 76 className="cursor-pointer hover:text-zinc-700 dark:hover:text-zinc-300" 77 /* @ts-ignore - Hyperscript attribute */ 78 _="on click toggle .hidden on next <pre/>" 79 > 80 <Text as="span" size="xs" variant="muted"> 81 View metadata 82 </Text> 83 </summary> 84 <pre className="mt-2 p-2 bg-zinc-100 dark:bg-zinc-800 rounded text-xs overflow-x-auto break-words whitespace-pre-wrap hidden"> 85 <Text as="span" size="xs"> 86 {JSON.stringify(log.metadata, null, 2)} 87 </Text> 88 </pre> 89 </details> 90 )} 91 </div> 92 </div> 93 </div> 94 ))} 95 </Card.Content> 96 </Card> 97 </> 98 ); 99}