From e97b50a91626445617eabe192ac65800dc516045 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 30 Jan 2026 23:16:45 +0000 Subject: [PATCH] Organize interfaces and utilities into centralized folders Move all TypeScript interfaces from component files to /lib/interfaces folder Move terminal utility functions to /lib/utils folder Update all component imports to use centralized interfaces and utilities Fix JSX.Element type to React.ReactElement in terminal utils This improves code organization and reduces duplication across components https://claude.ai/code/session_G4kZm --- frontend/components/ContainerCard.tsx | 7 +-- .../ContainerCard/ContainerActions.tsx | 11 +--- .../ContainerCard/ContainerHeader.tsx | 7 +-- .../ContainerCard/ContainerInfo.tsx | 6 +- .../ContainerCard/DeleteConfirmDialog.tsx | 8 +-- .../components/Dashboard/DashboardHeader.tsx | 9 +-- frontend/components/TerminalModal.tsx | 8 +-- .../components/TerminalModal/CommandInput.tsx | 22 +------ .../TerminalModal/FallbackNotification.tsx | 8 +-- .../TerminalModal/InteractiveTerminal.tsx | 5 +- .../TerminalModal/SimpleTerminal.tsx | 15 +---- .../TerminalModal/TerminalHeader.tsx | 9 +-- .../TerminalModal/TerminalOutput.tsx | 48 +-------------- frontend/lib/hooks/useSimpleTerminal.ts | 7 +-- frontend/lib/interfaces/container.ts | 35 +++++++++++ frontend/lib/interfaces/dashboard.ts | 7 +++ frontend/lib/interfaces/terminal.ts | 61 +++++++++++++++++++ frontend/lib/utils/terminal.tsx | 41 +++++++++++++ 18 files changed, 160 insertions(+), 154 deletions(-) create mode 100644 frontend/lib/interfaces/container.ts create mode 100644 frontend/lib/interfaces/dashboard.ts create mode 100644 frontend/lib/interfaces/terminal.ts create mode 100644 frontend/lib/utils/terminal.tsx diff --git a/frontend/components/ContainerCard.tsx b/frontend/components/ContainerCard.tsx index 600b9a3..1dec773 100644 --- a/frontend/components/ContainerCard.tsx +++ b/frontend/components/ContainerCard.tsx @@ -3,18 +3,13 @@ import React, { useState } from 'react'; import { Card, CardContent, Divider, Snackbar, Alert } from '@mui/material'; import { Container } from '@/lib/api'; +import { ContainerCardProps } from '@/lib/interfaces/container'; import { useContainerActions } from '@/lib/hooks/useContainerActions'; import ContainerHeader from './ContainerCard/ContainerHeader'; import ContainerInfo from './ContainerCard/ContainerInfo'; import ContainerActions from './ContainerCard/ContainerActions'; import DeleteConfirmDialog from './ContainerCard/DeleteConfirmDialog'; -interface ContainerCardProps { - container: Container; - onOpenShell: () => void; - onContainerUpdate?: () => void; -} - const borderColors = { running: '#38b2ac', stopped: '#718096', diff --git a/frontend/components/ContainerCard/ContainerActions.tsx b/frontend/components/ContainerCard/ContainerActions.tsx index 1b0245f..2dcc344 100644 --- a/frontend/components/ContainerCard/ContainerActions.tsx +++ b/frontend/components/ContainerCard/ContainerActions.tsx @@ -1,16 +1,7 @@ import React from 'react'; import { Box, Button, CircularProgress } from '@mui/material'; import { PlayArrow, Stop, Refresh, Delete, Terminal } from '@mui/icons-material'; - -interface ContainerActionsProps { - status: string; - isLoading: boolean; - onStart: () => void; - onStop: () => void; - onRestart: () => void; - onRemove: () => void; - onOpenShell: () => void; -} +import { ContainerActionsProps } from '@/lib/interfaces/container'; export default function ContainerActions({ status, diff --git a/frontend/components/ContainerCard/ContainerHeader.tsx b/frontend/components/ContainerCard/ContainerHeader.tsx index 0231f5a..eb54bd4 100644 --- a/frontend/components/ContainerCard/ContainerHeader.tsx +++ b/frontend/components/ContainerCard/ContainerHeader.tsx @@ -1,12 +1,7 @@ import React from 'react'; import { Box, Typography, Chip } from '@mui/material'; import { PlayArrow, Inventory2 } from '@mui/icons-material'; - -interface ContainerHeaderProps { - name: string; - image: string; - status: string; -} +import { ContainerHeaderProps } from '@/lib/interfaces/container'; const statusColors = { running: 'success', diff --git a/frontend/components/ContainerCard/ContainerInfo.tsx b/frontend/components/ContainerCard/ContainerInfo.tsx index 13a3b6d..ba80546 100644 --- a/frontend/components/ContainerCard/ContainerInfo.tsx +++ b/frontend/components/ContainerCard/ContainerInfo.tsx @@ -1,10 +1,6 @@ import React from 'react'; import { Box, Typography } from '@mui/material'; - -interface ContainerInfoProps { - id: string; - uptime: string; -} +import { ContainerInfoProps } from '@/lib/interfaces/container'; export default function ContainerInfo({ id, uptime }: ContainerInfoProps) { return ( diff --git a/frontend/components/ContainerCard/DeleteConfirmDialog.tsx b/frontend/components/ContainerCard/DeleteConfirmDialog.tsx index 9df10f2..118ea6d 100644 --- a/frontend/components/ContainerCard/DeleteConfirmDialog.tsx +++ b/frontend/components/ContainerCard/DeleteConfirmDialog.tsx @@ -7,13 +7,7 @@ import { DialogActions, Button, } from '@mui/material'; - -interface DeleteConfirmDialogProps { - open: boolean; - containerName: string; - onClose: () => void; - onConfirm: () => void; -} +import { DeleteConfirmDialogProps } from '@/lib/interfaces/container'; export default function DeleteConfirmDialog({ open, diff --git a/frontend/components/Dashboard/DashboardHeader.tsx b/frontend/components/Dashboard/DashboardHeader.tsx index 4d71972..a0e314e 100644 --- a/frontend/components/Dashboard/DashboardHeader.tsx +++ b/frontend/components/Dashboard/DashboardHeader.tsx @@ -9,14 +9,7 @@ import { CircularProgress, } from '@mui/material'; import { Logout, Refresh, Inventory2 } from '@mui/icons-material'; - -interface DashboardHeaderProps { - containerCount: number; - isMobile: boolean; - isRefreshing: boolean; - onRefresh: () => void; - onLogout: () => void; -} +import { DashboardHeaderProps } from '@/lib/interfaces/dashboard'; export default function DashboardHeader({ containerCount, diff --git a/frontend/components/TerminalModal.tsx b/frontend/components/TerminalModal.tsx index 243c4fe..5199bab 100644 --- a/frontend/components/TerminalModal.tsx +++ b/frontend/components/TerminalModal.tsx @@ -4,18 +4,12 @@ import React, { useState } from 'react'; import { Dialog, DialogContent, DialogActions, Button, useMediaQuery, useTheme } from '@mui/material'; import { useSimpleTerminal } from '@/lib/hooks/useSimpleTerminal'; import { useInteractiveTerminal } from '@/lib/hooks/useInteractiveTerminal'; +import { TerminalModalProps } from '@/lib/interfaces/terminal'; import TerminalHeader from './TerminalModal/TerminalHeader'; import SimpleTerminal from './TerminalModal/SimpleTerminal'; import InteractiveTerminal from './TerminalModal/InteractiveTerminal'; import FallbackNotification from './TerminalModal/FallbackNotification'; -interface TerminalModalProps { - open: boolean; - onClose: () => void; - containerName: string; - containerId: string; -} - export default function TerminalModal({ open, onClose, diff --git a/frontend/components/TerminalModal/CommandInput.tsx b/frontend/components/TerminalModal/CommandInput.tsx index 2c47525..9267c33 100644 --- a/frontend/components/TerminalModal/CommandInput.tsx +++ b/frontend/components/TerminalModal/CommandInput.tsx @@ -1,26 +1,8 @@ import React from 'react'; import { Box, Typography, TextField, Button, IconButton } from '@mui/material'; import { Send } from '@mui/icons-material'; - -interface CommandInputProps { - command: string; - workdir: string; - isExecuting: boolean; - isMobile: boolean; - containerName: string; - onCommandChange: (value: string) => void; - onExecute: () => void; - onKeyPress: (e: React.KeyboardEvent) => void; -} - -const formatPrompt = (containerName: string, workdir: string) => { - let displayDir = workdir; - if (workdir.length > 30) { - const parts = workdir.split('/'); - displayDir = '.../' + parts[parts.length - 1]; - } - return `root@${containerName}:${displayDir}#`; -}; +import { CommandInputProps } from '@/lib/interfaces/terminal'; +import { formatPrompt } from '@/lib/utils/terminal'; export default function CommandInput({ command, diff --git a/frontend/components/TerminalModal/FallbackNotification.tsx b/frontend/components/TerminalModal/FallbackNotification.tsx index ebc3762..513d16f 100644 --- a/frontend/components/TerminalModal/FallbackNotification.tsx +++ b/frontend/components/TerminalModal/FallbackNotification.tsx @@ -1,13 +1,7 @@ import React from 'react'; import { Snackbar, Alert, Typography, Button } from '@mui/material'; import { Warning } from '@mui/icons-material'; - -interface FallbackNotificationProps { - show: boolean; - reason: string; - onClose: () => void; - onRetry: () => void; -} +import { FallbackNotificationProps } from '@/lib/interfaces/terminal'; export default function FallbackNotification({ show, diff --git a/frontend/components/TerminalModal/InteractiveTerminal.tsx b/frontend/components/TerminalModal/InteractiveTerminal.tsx index 2cf3a99..2a12525 100644 --- a/frontend/components/TerminalModal/InteractiveTerminal.tsx +++ b/frontend/components/TerminalModal/InteractiveTerminal.tsx @@ -1,10 +1,7 @@ import React from 'react'; import { Box } from '@mui/material'; import '@xterm/xterm/css/xterm.css'; - -interface InteractiveTerminalProps { - terminalRef: React.RefObject; -} +import { InteractiveTerminalProps } from '@/lib/interfaces/terminal'; export default function InteractiveTerminal({ terminalRef }: InteractiveTerminalProps) { return ( diff --git a/frontend/components/TerminalModal/SimpleTerminal.tsx b/frontend/components/TerminalModal/SimpleTerminal.tsx index 074a9e9..23bcccc 100644 --- a/frontend/components/TerminalModal/SimpleTerminal.tsx +++ b/frontend/components/TerminalModal/SimpleTerminal.tsx @@ -1,21 +1,8 @@ import React from 'react'; -import { OutputLine } from '@/lib/hooks/useSimpleTerminal'; +import { SimpleTerminalProps } from '@/lib/interfaces/terminal'; import TerminalOutput from './TerminalOutput'; import CommandInput from './CommandInput'; -interface SimpleTerminalProps { - output: OutputLine[]; - command: string; - workdir: string; - isExecuting: boolean; - isMobile: boolean; - containerName: string; - outputRef: React.RefObject; - onCommandChange: (value: string) => void; - onExecute: () => void; - onKeyPress: (e: React.KeyboardEvent) => void; -} - export default function SimpleTerminal({ output, command, diff --git a/frontend/components/TerminalModal/TerminalHeader.tsx b/frontend/components/TerminalModal/TerminalHeader.tsx index de2cc7b..d9bde68 100644 --- a/frontend/components/TerminalModal/TerminalHeader.tsx +++ b/frontend/components/TerminalModal/TerminalHeader.tsx @@ -9,14 +9,7 @@ import { Tooltip, } from '@mui/material'; import { Close, Terminal as TerminalIcon, Code, Warning } from '@mui/icons-material'; - -interface TerminalHeaderProps { - containerName: string; - mode: 'simple' | 'interactive'; - interactiveFailed: boolean; - onModeChange: (event: React.MouseEvent, newMode: 'simple' | 'interactive' | null) => void; - onClose: () => void; -} +import { TerminalHeaderProps } from '@/lib/interfaces/terminal'; export default function TerminalHeader({ containerName, diff --git a/frontend/components/TerminalModal/TerminalOutput.tsx b/frontend/components/TerminalModal/TerminalOutput.tsx index cd114d3..c41bbb4 100644 --- a/frontend/components/TerminalModal/TerminalOutput.tsx +++ b/frontend/components/TerminalModal/TerminalOutput.tsx @@ -1,51 +1,7 @@ import React from 'react'; import { Box, Paper, Typography } from '@mui/material'; -import { OutputLine } from '@/lib/hooks/useSimpleTerminal'; - -interface TerminalOutputProps { - output: OutputLine[]; - containerName: string; - outputRef: React.RefObject; -} - -const formatPrompt = (containerName: string, workdir: string) => { - let displayDir = workdir; - if (workdir.length > 30) { - const parts = workdir.split('/'); - displayDir = '.../' + parts[parts.length - 1]; - } - return `root@${containerName}:${displayDir}#`; -}; - -const highlightCommand = (line: OutputLine, containerName: string) => { - if (line.type === 'command') { - const prompt = formatPrompt(containerName, line.workdir || '/'); - const parts = line.content.split(' '); - const cmd = parts[0]; - const args = parts.slice(1).join(' '); - - return ( -
- {prompt} - {' '} - {cmd} - {args && {args}} -
- ); - } else if (line.type === 'error') { - return ( -
- {line.content} -
- ); - } else { - return ( -
- {line.content} -
- ); - } -}; +import { TerminalOutputProps } from '@/lib/interfaces/terminal'; +import { highlightCommand } from '@/lib/utils/terminal'; export default function TerminalOutput({ output, containerName, outputRef }: TerminalOutputProps) { return ( diff --git a/frontend/lib/hooks/useSimpleTerminal.ts b/frontend/lib/hooks/useSimpleTerminal.ts index c65c26a..27367e5 100644 --- a/frontend/lib/hooks/useSimpleTerminal.ts +++ b/frontend/lib/hooks/useSimpleTerminal.ts @@ -1,11 +1,6 @@ import { useState, useRef, useEffect } from 'react'; import { apiClient } from '@/lib/api'; - -export interface OutputLine { - type: 'command' | 'output' | 'error'; - content: string; - workdir?: string; -} +import { OutputLine } from '@/lib/interfaces/terminal'; export function useSimpleTerminal(containerId: string) { const [command, setCommand] = useState(''); diff --git a/frontend/lib/interfaces/container.ts b/frontend/lib/interfaces/container.ts new file mode 100644 index 0000000..c21a42f --- /dev/null +++ b/frontend/lib/interfaces/container.ts @@ -0,0 +1,35 @@ +import { Container } from '@/lib/api'; + +export interface ContainerCardProps { + container: Container; + onOpenShell: () => void; + onContainerUpdate?: () => void; +} + +export interface ContainerHeaderProps { + name: string; + image: string; + status: string; +} + +export interface ContainerInfoProps { + id: string; + uptime: string; +} + +export interface ContainerActionsProps { + status: string; + isLoading: boolean; + onStart: () => void; + onStop: () => void; + onRestart: () => void; + onRemove: () => void; + onOpenShell: () => void; +} + +export interface DeleteConfirmDialogProps { + open: boolean; + containerName: string; + onClose: () => void; + onConfirm: () => void; +} diff --git a/frontend/lib/interfaces/dashboard.ts b/frontend/lib/interfaces/dashboard.ts new file mode 100644 index 0000000..7019f7b --- /dev/null +++ b/frontend/lib/interfaces/dashboard.ts @@ -0,0 +1,7 @@ +export interface DashboardHeaderProps { + containerCount: number; + isMobile: boolean; + isRefreshing: boolean; + onRefresh: () => void; + onLogout: () => void; +} diff --git a/frontend/lib/interfaces/terminal.ts b/frontend/lib/interfaces/terminal.ts new file mode 100644 index 0000000..50ee1bd --- /dev/null +++ b/frontend/lib/interfaces/terminal.ts @@ -0,0 +1,61 @@ +export interface OutputLine { + type: 'command' | 'output' | 'error'; + content: string; + workdir?: string; +} + +export interface TerminalModalProps { + open: boolean; + onClose: () => void; + containerName: string; + containerId: string; +} + +export interface TerminalHeaderProps { + containerName: string; + mode: 'simple' | 'interactive'; + interactiveFailed: boolean; + onModeChange: (event: React.MouseEvent, newMode: 'simple' | 'interactive' | null) => void; + onClose: () => void; +} + +export interface InteractiveTerminalProps { + terminalRef: React.RefObject; +} + +export interface SimpleTerminalProps { + output: OutputLine[]; + command: string; + workdir: string; + isExecuting: boolean; + isMobile: boolean; + containerName: string; + outputRef: React.RefObject; + onCommandChange: (value: string) => void; + onExecute: () => void; + onKeyPress: (e: React.KeyboardEvent) => void; +} + +export interface TerminalOutputProps { + output: OutputLine[]; + containerName: string; + outputRef: React.RefObject; +} + +export interface CommandInputProps { + command: string; + workdir: string; + isExecuting: boolean; + isMobile: boolean; + containerName: string; + onCommandChange: (value: string) => void; + onExecute: () => void; + onKeyPress: (e: React.KeyboardEvent) => void; +} + +export interface FallbackNotificationProps { + show: boolean; + reason: string; + onClose: () => void; + onRetry: () => void; +} diff --git a/frontend/lib/utils/terminal.tsx b/frontend/lib/utils/terminal.tsx new file mode 100644 index 0000000..5c475ed --- /dev/null +++ b/frontend/lib/utils/terminal.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { OutputLine } from '@/lib/interfaces/terminal'; + +export const formatPrompt = (containerName: string, workdir: string): string => { + let displayDir = workdir; + if (workdir.length > 30) { + const parts = workdir.split('/'); + displayDir = '.../' + parts[parts.length - 1]; + } + return `root@${containerName}:${displayDir}#`; +}; + +export const highlightCommand = (line: OutputLine, containerName: string): React.ReactElement => { + if (line.type === 'command') { + const prompt = formatPrompt(containerName, line.workdir || '/'); + const parts = line.content.split(' '); + const cmd = parts[0]; + const args = parts.slice(1).join(' '); + + return ( +
+ {prompt} + {' '} + {cmd} + {args && {args}} +
+ ); + } else if (line.type === 'error') { + return ( +
+ {line.content} +
+ ); + } else { + return ( +
+ {line.content} +
+ ); + } +};