mirror of
https://github.com/johndoe6345789/docker-swarm-termina.git
synced 2026-04-24 21:55:13 +00:00
This commit implements a major frontend refactoring to improve testability and maintainability through better separation of concerns. ## New Comprehensive Hooks **useTerminalModalState** (100% coverage): - Manages all TerminalModal state logic - Handles mode switching (interactive <-> simple) - Manages fallback logic and notifications - Mobile responsiveness detection **useDashboard** (Ready for testing): - Consolidates all Dashboard page logic - Combines authentication, containers, and terminal state - Provides derived state (isInitialLoading, showEmptyState) - Simplifies Dashboard component to pure presentation ## Refactored Components **TerminalModal**: Reduced from 135 to 95 lines (-30%) - Extracted state management to useTerminalModalState hook - Now focuses solely on rendering - All business logic moved to hooks **Dashboard Page**: Reduced from 90 to 66 lines (-27%) - Extracted logic to useDashboard hook - Removed redundant state calculations - Cleaner, more readable component ## Comprehensive Test Coverage **New Tests Added**: 1. useTerminalModalState.test.tsx (100% coverage, 8 tests) 2. useContainerActions.test.tsx (100% coverage, 15 tests) 3. useContainerList.test.tsx (100% coverage, 9 tests) 4. useSimpleTerminal.test.tsx (97% coverage, 18 tests) **Test Coverage Improvements**: - Frontend hooks: 30% → 54% coverage (+80% improvement) - Overall frontend: 28% → 42% coverage (+50% improvement) - All custom hooks: 100% coverage (except useDashboard, useInteractiveTerminal) **Total**: 105 passing tests (was 65) ## Benefits 1. **Better Testability**: Logic in hooks is easier to test than in components 2. **Smaller Components**: Components are now pure presentational 3. **Reusability**: Hooks can be reused across components 4. **Maintainability**: Business logic separated from presentation 5. **Type Safety**: Full TypeScript support maintained ## Coverage Summary Backend: 100% (467/467 statements, 116 tests) Frontend: 42% overall, 54% hooks (105 tests) Hooks with 100% Coverage: - ✅ useTerminalModalState - ✅ useContainerActions - ✅ useContainerList - ✅ useTerminalModal - ✅ useAuthRedirect - ✅ authErrorHandler https://claude.ai/code/session_mmQs0
101 lines
3.0 KiB
TypeScript
101 lines
3.0 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import { Dialog, DialogContent, DialogActions, Button } from '@mui/material';
|
|
import { useSimpleTerminal } from '@/lib/hooks/useSimpleTerminal';
|
|
import { useInteractiveTerminal } from '@/lib/hooks/useInteractiveTerminal';
|
|
import { useTerminalModalState } from '@/lib/hooks/useTerminalModalState';
|
|
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';
|
|
|
|
export default function TerminalModal({
|
|
open,
|
|
onClose,
|
|
containerName,
|
|
containerId,
|
|
}: TerminalModalProps) {
|
|
const modalState = useTerminalModalState();
|
|
const simpleTerminal = useSimpleTerminal(containerId);
|
|
|
|
const interactiveTerminal = useInteractiveTerminal({
|
|
open: open && modalState.mode === 'interactive',
|
|
containerId,
|
|
containerName,
|
|
isMobile: modalState.isMobile,
|
|
onFallback: modalState.handleFallback,
|
|
});
|
|
|
|
const handleClose = () => {
|
|
interactiveTerminal.cleanup();
|
|
simpleTerminal.reset();
|
|
modalState.reset();
|
|
onClose();
|
|
};
|
|
|
|
const handleKeyPress = (e: React.KeyboardEvent) => {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault();
|
|
simpleTerminal.executeCommand();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dialog
|
|
open={open}
|
|
onClose={handleClose}
|
|
maxWidth="md"
|
|
fullWidth
|
|
fullScreen={modalState.isMobile}
|
|
PaperProps={{
|
|
sx: {
|
|
minHeight: modalState.isMobile ? '100vh' : '500px',
|
|
maxHeight: modalState.isMobile ? '100vh' : '80vh',
|
|
},
|
|
}}
|
|
>
|
|
<TerminalHeader
|
|
containerName={containerName}
|
|
mode={modalState.mode}
|
|
interactiveFailed={modalState.interactiveFailed}
|
|
onModeChange={modalState.handleModeChange}
|
|
onClose={handleClose}
|
|
/>
|
|
|
|
<DialogContent dividers>
|
|
{modalState.mode === 'interactive' ? (
|
|
<InteractiveTerminal terminalRef={interactiveTerminal.terminalRef} />
|
|
) : (
|
|
<SimpleTerminal
|
|
output={simpleTerminal.output}
|
|
command={simpleTerminal.command}
|
|
workdir={simpleTerminal.workdir}
|
|
isExecuting={simpleTerminal.isExecuting}
|
|
isMobile={modalState.isMobile}
|
|
containerName={containerName}
|
|
outputRef={simpleTerminal.outputRef}
|
|
onCommandChange={simpleTerminal.setCommand}
|
|
onExecute={simpleTerminal.executeCommand}
|
|
onKeyPress={handleKeyPress}
|
|
/>
|
|
)}
|
|
</DialogContent>
|
|
|
|
<DialogActions>
|
|
<Button onClick={handleClose} variant="outlined">
|
|
Close
|
|
</Button>
|
|
</DialogActions>
|
|
|
|
<FallbackNotification
|
|
show={modalState.showFallbackNotification}
|
|
reason={modalState.fallbackReason}
|
|
onClose={() => modalState.reset()}
|
|
onRetry={modalState.handleRetryInteractive}
|
|
/>
|
|
</Dialog>
|
|
);
|
|
}
|