Commit b73921eb authored by avelichko's avatar avelichko

Правки

parent 46f27ebd
{
"git.ignoreLimitWarning": true
}
\ No newline at end of file
This diff is collapsed.
from datetime import datetime
from typing import Any, Dict, List, Optional, Union
from enum import Enum
class Function:
def __init__(self, id: str, name: str, language: str, platform: str) -> None:
self.id: str = id
self.name: str = name
self.language: str = language
self.platform: str = platform
class Version:
def __init__(
self, function: Function, version_number: str, origin: str, code: str, created_at: datetime
) -> None:
self.function: Function = function
self.version_number: str = version_number
self.origin: str = origin
self.code: str = code
self.created_at: datetime = created_at
class OptimizationReport:
def __init__(
self,
version: Version,
report_id: str,
recommendations: List[str],
optimized_code: str,
metrics_before: Dict[str, Any],
metrics_after: Dict[str, Any],
created_at: datetime,
) -> None:
self.version: Version = version
self.report_id: str = report_id
self.recommendations: List[str] = recommendations
self.optimized_code: str = optimized_code
self.metrics_before: Dict[str, Any] = metrics_before
self.metrics_after: Dict[str, Any] = metrics_after
self.created_at: datetime = created_at
class TestConfiguration:
def __init__(
self, version: Version, runtime: str, iterations: int, parallelism: int, timeout: int
) -> None:
self.version: Version = version
self.runtime: str = runtime
self.iterations: int = iterations
self.parallelism: int = parallelism
self.timeout: int = timeout
class TestRunStatus(Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
class TestRun:
def __init__(
self,
test_configuration: TestConfiguration,
run_id: str,
started_at: datetime,
ended_at: Optional[datetime] = None,
status: TestRunStatus = TestRunStatus.PENDING,
) -> None:
self.test_configuration: TestConfiguration = test_configuration
self.run_id: str = run_id
self.started_at: datetime = started_at
self.ended_at: Optional[datetime] = ended_at
self.status: TestRunStatus = status
class Metric:
def __init__(
self, test_run: TestRun, type: str, percentiles: Dict[str, float], timestamp: datetime
) -> None:
self.test_run: TestRun = test_run
self.type: str = type
self.percentiles: Dict[str, float] = percentiles
self.timestamp: datetime = timestamp
class TestProfile:
def __init__(
self,
test_run: TestRun,
profile_id: str,
function_name: str,
version_number: str,
run_timestamp: datetime,
) -> None:
self.test_run: TestRun = test_run
self.profile_id: str = profile_id
self.function_name: str = function_name
self.version_number: str = version_number
self.run_timestamp: datetime = run_timestamp
class Draft:
def __init__(self, version: Version, content: str, last_updated: datetime) -> None:
self.version: Version = version
self.content: str = content
self.last_updated: datetime = last_updated
\ No newline at end of file
from typing import Any
from dataclasses import dataclass
@dataclass
class Result:
data: Any
@dataclass
class Metrics:
time: int
resources: dict[str, int]
@dataclass
class ComparisonReport:
improvements: dict[str, float]
class TestScenario:
def __init__(self, id: str, name: str, configuration: dict[str, Any]) -> None:
self.id = id
self.name = name
self.configuration = configuration
def execute(self) -> Result:
pass
class Optimizer:
def optimize(self, scenario: TestScenario) -> TestScenario:
pass
class MetricsCollector:
def collect_metrics(self, result: Result) -> Metrics:
pass
class ComparisonTool:
def compare(self, before: Metrics, after: Metrics) -> ComparisonReport:
pass
class RecommendationGenerator:
def generate_recommendations(self, report: ComparisonReport) -> list[str]:
pass
class WebPortal:
def show_results(self, report: ComparisonReport) -> None:
pass
def show_recommendations(self, recommendations: list[str]) -> None:
pass
\ No newline at end of file
diagrams/ClassDiagram.png

71.3 KB | W: | H:

diagrams/ClassDiagram.png

45 KB | W: | H:

diagrams/ClassDiagram.png
diagrams/ClassDiagram.png
diagrams/ClassDiagram.png
diagrams/ClassDiagram.png
  • 2-up
  • Swipe
  • Onion skin
diagrams/UseCase.png

29.4 KB | W: | H:

diagrams/UseCase.png

71.9 KB | W: | H:

diagrams/UseCase.png
diagrams/UseCase.png
diagrams/UseCase.png
diagrams/UseCase.png
  • 2-up
  • Swipe
  • Onion skin
@startuml ActivityDiagram
title Диаграмма активности процесса тестирования Serverless
start
:Настроить тестовый сценарий;
:Оптимизировать сценарий;
:Запустить оптимизированный тест;
:Собрать метрики оптимизированного теста;
:Запустить оригинальный тест;
:Собрать метрики оригинального теста;
:Сравнить метрики;
:Сгенерировать рекомендации;
:Отобразить результаты;
@startuml Activity_Editor
title Диаграмма деятельности: Редактор кода
partition Пользователь {
start
:Открыть страницу "Редактор";
if (Режим = Create?) then (да)
:Заполнить "Название";
:Выбрать "Язык" и "Платформу";
:Ввести код функции;
if (Код ≠ пустой) then (да)
:Нажать "Сохранить";
else
:Показать ошибку "Код не может быть пустым";
stop
endif
else (нет)
:Просмотреть существующий код;
endif
:Нажать кнопку "Редактировать" или "Оптимизировать";
}
partition Система {
if (Нажали "Оптимизировать") then (да)
:Отправить код в LLM;
:Получить оптимизированный код и рекомендации;
:Перейти в режим ViewOptimized;
elseif (Нажали "Редактировать")
:Перейти в режим Edit;
endif
if (В режиме Edit и есть изменения?) then (да)
:Сохранить новую версию;
:Перейти в режим View;
endif
}
stop
@enduml
@startuml Activity_Testing
title Диаграмма деятельности: Тестирование
partition Пользователь {
start
:Открыть страницу "Тестирование";
:Выбрать функцию и версию;
:Задать параметры теста (рантайм, итерации, параллельность, таймаут);
if (Все поля заполнены?) then (да)
:Нажать "Запустить тестирование";
else
:Показать тултип "Заполните все поля";
stop
endif
}
partition Система {
:Отправить запрос на запуск тестов;
:Отобразить прогресс-бар и секундомер;
while (Тестирование в процессе)
:Собирать метрики;
endwhile
}
partition Пользователь {
if (Нажали "Отмена" во время теста?) then (да)
:Подтвердить отмену;
stop
endif
}
partition Система {
:Тестирование завершено;
}
partition Пользователь {
:Показать экран "Результаты тестирования";
if (Нажали "Просмотр результатов") then (да)
:Перейти на вкладку "Результаты";
elseif (Нажали "Новый тест")
:Вернуться к конфигурации;
endif
}
stop
@enduml
@startuml Activity_Results
title Диаграмма деятельности: Результаты
partition Пользователь {
start
:Открыть страницу "Результаты";
:Показать два селектора профилей;
:Ожидать выбор в Селекторе 1;
if (Профиль 1 выбран?) then (да)
:Разблокировать Селектор 2;
else
stop
endif
:Ожидать выбор в Селекторе 2?;
}
partition Система {
if (Профиль 1 выбран) then
:Загрузить и отобразить данные профиля 1;
:Показать графики и "Детали теста 1";
endif
if (Профиль 2 выбран) then
:Загрузить и отобразить данные профиля 2;
:Показать графики сравнения;
:Показать "Детали теста 2" и "Сводку изменений";
endif
}
partition Пользователь {
:Сбросить селекторы или закрыть страницу;
}
stop
@enduml
\ No newline at end of file
@startuml ClassDiagram
title Диаграмма классов системы тестирования Serverless
class СценарийТеста {
+id: String
+имя: String
+конфигурация: Map<String, Object>
+выполнить(): Результат
title Диаграмма классов: Система тестирования Serverless
class Function {
+id
+name
+language
+platform
}
class Оптимизатор {
+оптимизировать(сценарий: СценарийТеста): СценарийТеста
}
class СборщикМетрик {
+собратьМетрики(результат: Результат): Метрики
class Version {
+versionNumber
+origin
+code
+createdAt
}
class ИнструментСравнения {
+сравнить(до: Метрики, после: Метрики): ОтчетСравнения
class OptimizationReport {
+reportId
+recommendations
+optimizedCode
+metricsBefore
+metricsAfter
+createdAt
}
class ГенераторРекомендаций {
+сгенерироватьРекомендации(отчет: ОтчетСравнения): List<String>
class TestConfiguration {
+runtime
+iterations
+parallelism
+timeout
}
class ВебПортал {
+показатьРезультаты(отчет: ОтчетСравнения)
+показатьРекомендации(рекомендации: List<String>)
class TestRun {
+runId
+startedAt
+endedAt
+status
}
class Результат {
+данные: Object
class Metric {
+type
+percentiles
+timestamp
}
class Метрики {
+время: Integer
+ресурсы: Map<String, Integer>
class TestProfile {
+profileId
+functionName
+versionNumber
+runTimestamp
}
class ОтчетСравнения {
+улучшения: Map<String, Double>
class Draft {
+content
+lastUpdated
}
СценарийТеста --> Результат : "1"
Оптимизатор --> СценарийТеста : оптимизирует
СборщикМетрик --> Метрики : собирает
ИнструментСравнения --> ОтчетСравнения : создает отчет
ГенераторРекомендаций --> "List<String>" : рекомендации
ВебПортал --> ОтчетСравнения : отображает отчет
ВебПортал --> "List<String>" : отображает рекомендации
Function "1" -- "0..*" Version
Version "1" -- "0..1" OptimizationReport
Version "1" -- "0..*" TestConfiguration
TestConfiguration "1" -- "1..*" TestRun
TestRun "1" -- "0..*" Metric
TestRun "1" -- "0..1" TestProfile
Version "1" -- "0..1" Draft
@enduml
\ No newline at end of file
@startuml SequenceDiagram
title Диаграмма последовательности выполнения тестов Serverless
actor ОблачныйИнженер
participant ВебПортал
participant ДвижокТестирования as TestEngine
participant Оптимизатор
participant СборщикМетрик
participant ИнструментСравнения
participant ГенераторРекомендаций
@startuml Sequence_Optimization
title Диаграмма последовательности: Оптимизация кода
actor "Разработчик" as Dev
participant "Веб-интерфейс" as UI
participant "Backend-сервис" as Backend
participant "LLM-модуль" as LLM
ОблачныйИнженер -> ВебПортал: настроитьТест(конфигСценария)
ВебПортал -> ДвижокТестирования: запуститьТест(конфигСценария)
ДвижокТестирования -> Оптимизатор: оптимизировать(сценарий)
Оптимизатор --> ДвижокТестирования: оптимизированныйСценарий
ДвижокТестирования -> СборщикМетрик: собратьМетрики(оптимизированныйСценарий)
СборщикМетрик --> ДвижокТестирования: метрикиОптимизированного
ДвижокТестирования --> ВебПортал: результатыОптимизированного
ВебПортал -> ДвижокТестирования: запуститьТест(оригСценарий)
ДвижокТестирования -> СборщикМетрик: собратьМетрики(оригСценарий)
СборщикМетрик --> ДвижокТестирования: метрикиОригинала
ДвижокТестирования -> ИнструментСравнения: сравнить(метрикиОригинала, метрикиОптимизированного)
ИнструментСравнения --> ВебПортал: отчетСравнения
ВебПортал -> ГенераторРекомендаций: сгенерироватьРекомендации(отчетСравнения)
ГенераторРекомендаций --> ВебПортал: рекомендации
ВебПортал --> ОблачныйИнженер: отобразить(отчетСравнения, рекомендации)
Dev -> UI : Нажатие кнопки "Оптимизировать"
UI -> Backend : Отправить код функции
Backend -> LLM : Запрос на оптимизацию
LLM --> Backend : Переработанный код + рекомендации
Backend --> UI : Отобразить новую версию и блок "Оптимизации"
@enduml
@startuml Sequence_Testing
title Диаграмма последовательности: Запуск тестирования
actor "Инженер по качеству" as QA
participant "Веб-интерфейс" as UI
participant "Backend-сервис" as Backend
participant "Облачная платформа" as Cloud
participant "Модуль сбора метрик" as Metrics
QA -> UI : Нажать "Запустить тестирование"
UI -> Backend : Передать параметры теста
Backend -> Cloud : Запустить функцию (итерации, параллельность)
Cloud --> Metrics : Передача метрик во время выполнения
Metrics --> Backend : Сбор и агрегация результатов
Backend --> UI : Передать данные для отображения
@enduml
@startuml Sequence_Results
title Диаграмма последовательности: Сравнение результатов
actor "Разработчик" as Dev
participant "Веб-интерфейс" as UI
participant "Backend-сервис" as Backend
Dev -> UI : Выбор двух профилей тестирования
UI -> Backend : Запрос данных двух профилей
Backend --> UI : Данные профилей + дельты
UI --> Dev : Отобразить графики и "Сводку изменений"
@enduml
\ No newline at end of file
@startuml StateDiagram
title Диаграмма состояний процесса тестирования Serverless
[*] --> Создано
Создано --> Оптимизировано : оптимизировать()
Оптимизировано --> Выполняется : выполнить()
Выполняется --> МетрикиСобраны : собратьМетрики()
МетрикиСобраны --> Сравнение : сравнить()
Сравнение --> Рекомендации : сгенерироватьРекомендации()
Рекомендации --> Просмотр : просмотреть()
Просмотр --> [*]
@startuml State_Editor
title Диаграмма состояний: Редактор кода
skinparam state {
BackgroundColor #FDF6E3
BorderColor #586E75
}
[*] --> Create : Открыть страницу в режиме Create
state Create {
[*] --> Создание
Создание --> Сохранено : Нажать «Сохранить» (код ≠ пустой)
}
Create --> View : Сохранено → View
state View {
[*] --> Просмотр
Просмотр --> Edit : Нажать «Редактировать»
Просмотр --> ViewOptimized : Нажать «Оптимизировать»
}
state ViewOptimized {
[*] --> Оптимизировано
Оптимизировано --> Edit : Нажать «Редактировать»
}
state Edit {
[*] --> Редактирование
Редактирование --> View : Нажать «Сохранить» (есть изменения)
}
@enduml
@startuml State_Testing
title Диаграмма состояний: Тестирование
skinparam state {
BackgroundColor #EEF6F7
BorderColor #2AA198
}
[*] --> Configuration : Открыть страницу «Тестирование»
state Configuration {
[*] --> Конфигурирование
Конфигурирование --> Testing : Нажать «Запустить тестирование» (все поля заполнены)
}
state Testing {
[*] --> Запущено
Запущено --> Configuration : Отмена пользователя
Запущено --> Results : Тестирование завершено
}
state Results {
[*] --> Отображение
Отображение --> Configuration : Нажать «Новый тест»
Отображение --> ResultsPage : Нажать «Просмотр результатов»
}
@enduml
@startuml State_Results
title Диаграмма состояний: Результаты
skinparam state {
BackgroundColor #ECEBE4
BorderColor #657B83
}
[*] --> Selection : Открыть страницу «Результаты»
state Selection {
[*] --> ОжиданиеВыбора
ОжиданиеВыбора --> View : Выбрать профиль в Селекторе 1
}
state View {
[*] --> Просмотр
Просмотр --> Selection : Сбросить Селектор 1
Просмотр --> Compare : Выбрать профиль в Селекторе 2
}
state Compare {
[*] --> Сравнение
Сравнение --> View : Сбросить Селектор 2
Сравнение --> Selection : Сбросить Селектор 1
}
@enduml
\ No newline at end of file
@startuml UseCase
title Диаграмма вариантов использования комплекса тестирования Serverless
title Диаграмма вариантов использования: Тестирование Serverless
left to right direction
actor ОблачныйИнженер as "Облачный инженер"
rectangle "Комплекс тестирования Serverless" {
(Настроить тестовые сценарии) as UC1
(Запустить тесты) as UC2
(Просмотреть результаты) as UC3
(Сгенерировать рекомендации) as UC4
}
ОблачныйИнженер --> UC1
ОблачныйИнженер --> UC2
ОблачныйИнженер --> UC3
ОблачныйИнженер --> UC4
skinparam packageStyle rectangle
actor "Разработчик" as Developer
actor "Инженер по качеству" as QA
rectangle "Управление функциями" {
usecase "Создать новую функцию" as UC1
usecase "Просмотреть список функций" as UC2
usecase "Редактировать функцию" as UC3
}
rectangle "Оптимизация функции" {
usecase "Запустить авто-оптимизацию" as UC4
usecase "Применить рекомендации LLM" as UC5
}
rectangle "Тестирование" {
usecase "Настроить параметры тестирования" as UC6
usecase "Запустить нагрузочное тестирование" as UC7
}
rectangle "Анализ результатов" {
usecase "Просмотреть результаты теста" as UC8
usecase "Сравнить профили тестирования" as UC9
}
Developer --> UC1
Developer --> UC2
Developer --> UC3
Developer --> UC4
Developer --> UC5
QA --> UC6
QA --> UC7
Developer --> UC8
Developer --> UC9
@enduml
# Node.js
node_modules/
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
jspm_packages/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn
.yarn-integrity
# dotenv environment variables file
.env
# MacOS
.DS_Store
# Editor directories and files
.idea/
.vscode/
*.sublime-workspace
# Build directories
build/
dist/
# Docker
*.container
*.tar
\ No newline at end of file
FROM node:20-alpine
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# No need to copy source files as they will be mounted as a volume
# Expose port for development server
EXPOSE 3000
# Start development server with hot-reloading
CMD ["npm", "start"]
\ No newline at end of file
......@@ -2,10 +2,18 @@ version: '3.8'
services:
frontend:
build: .
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "8080:80"
- "3000:3000"
restart: unless-stopped
volumes:
- ./:/app
- /app/node_modules
environment:
- CHOKIDAR_USEPOLLING=true
- NODE_ENV=development
networks:
- serverless-net
......
This diff is collapsed.
......@@ -6,7 +6,9 @@
"@emotion/react": "^11.11.0",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.11.16",
"@mui/lab": "^5.0.0-alpha.153",
"@mui/material": "^5.13.2",
"@uiw/react-textarea-code-editor": "^3.1.1",
"chart.js": "^4.3.0",
"react": "^18.2.0",
"react-chartjs-2": "^5.2.0",
......
......@@ -12,6 +12,7 @@
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<title>Платформа тестирования Serverless</title>
</head>
<body>
......
\ No newline at end of file
......@@ -4,10 +4,10 @@ import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import Layout from './components/Layout';
import HomePage from './pages/HomePage';
import FunctionsPage from './pages/FunctionsPage';
import OptimizationPage from './pages/OptimizationPage';
import TestingPage from './pages/TestingPage';
import ResultsPage from './pages/ResultsPage';
import ConfigurationPage from './pages/ConfigurationPage';
const theme = createTheme({
palette: {
......@@ -32,10 +32,13 @@ function App() {
<CssBaseline />
<Layout>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/" element={<FunctionsPage />} />
<Route path="/optimization" element={<OptimizationPage />} />
<Route path="/optimization/:functionId" element={<OptimizationPage />} />
<Route path="/optimization/:functionId/:version" element={<OptimizationPage />} />
<Route path="/testing" element={<TestingPage />} />
<Route path="/testing/:functionId/:version" element={<TestingPage />} />
<Route path="/results" element={<ResultsPage />} />
<Route path="/configuration" element={<ConfigurationPage />} />
</Routes>
</Layout>
</ThemeProvider>
......
......@@ -15,18 +15,18 @@ import {
useTheme,
} from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import HomeIcon from '@mui/icons-material/Home';
import FunctionsIcon from '@mui/icons-material/Code';
import BuildIcon from '@mui/icons-material/Build';
import SpeedIcon from '@mui/icons-material/Speed';
import BarChartIcon from '@mui/icons-material/BarChart';
import SettingsIcon from '@mui/icons-material/Settings';
const drawerWidth = 240;
const menuItems = [
{ text: 'Главная', icon: <HomeIcon />, path: '/' },
{ text: 'Функции', icon: <FunctionsIcon />, path: '/' },
{ text: 'Оптимизация', icon: <BuildIcon />, path: '/optimization' },
{ text: 'Тестирование', icon: <SpeedIcon />, path: '/testing' },
{ text: 'Результаты', icon: <BarChartIcon />, path: '/results' },
{ text: 'Конфигурация', icon: <SettingsIcon />, path: '/configuration' },
];
function Layout({ children }) {
......@@ -40,11 +40,18 @@ function Layout({ children }) {
setMobileOpen(!mobileOpen);
};
const isPathActive = (path) => {
if (path === '/') {
return location.pathname === '/';
}
return location.pathname.startsWith(path);
};
const drawer = (
<div>
<Toolbar sx={{ justifyContent: 'center' }}>
<Typography variant="h6" component="div" fontWeight="bold">
ServerlessTest
Serverless Optimizer
</Typography>
</Toolbar>
<List>
......@@ -53,7 +60,7 @@ function Layout({ children }) {
button
key={item.text}
onClick={() => navigate(item.path)}
selected={location.pathname === item.path}
selected={isPathActive(item.path)}
>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.text} />
......@@ -83,7 +90,7 @@ function Layout({ children }) {
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Тестирование Serverless-функций
Оптимизация выполнения serverless-функций
</Typography>
</Toolbar>
</AppBar>
......
......@@ -25,3 +25,34 @@ code {
height: 300px;
position: relative;
}
/* Code Editor Styles */
.w-tc-editor {
position: relative;
}
.line-numbers > div {
height: 21px; /* Match the line-height of the editor */
}
/* Ensure editor lines match height with line numbers */
.w-tc-editor pre {
line-height: 1.5;
}
/* Ensure consistent scrolling */
.w-tc-editor-text {
overflow-y: auto !important;
}
/* Enforce JetBrains Mono for the editor */
.w-tc-editor-text,
.w-tc-editor-preview {
font-family: 'JetBrains Mono', monospace !important;
}
/* Dark mode support */
.w-tc-editor[data-color-mode="dark"] .w-tc-editor-text > div::before {
color: #666;
border-right-color: #444;
}
\ No newline at end of file
......@@ -14,4 +14,7 @@ root.render(
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
\ No newline at end of file
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
Box,
Button,
Container,
Divider,
IconButton,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
Modal,
Paper,
Tooltip,
Typography,
Collapse,
} from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
import CodeIcon from '@mui/icons-material/Code';
import JavascriptIcon from '@mui/icons-material/Javascript';
import CloudIcon from '@mui/icons-material/Cloud';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import VisibilityIcon from '@mui/icons-material/Visibility';
import SpeedIcon from '@mui/icons-material/Speed';
import Chip from '@mui/material/Chip';
// Моковые данные для отображения
const mockFunctions = [
{
id: 'func1',
name: 'API Gateway Handler',
language: 'python',
platform: 'yandex',
versions: [
{ version: 1, type: 'manual', createdAt: '2024-03-10' },
{ version: 2, type: 'auto', createdAt: '2024-03-11' },
{ version: 3, type: 'manual', createdAt: '2024-03-12' }
]
},
{
id: 'func2',
name: 'Database Worker',
language: 'nodejs',
platform: 'yandex',
versions: [
{ version: 1, type: 'manual', createdAt: '2024-03-14' },
{ version: 2, type: 'auto', createdAt: '2024-03-15' }
]
},
{
id: 'func3',
name: 'Image Processor',
language: 'python',
platform: 'yandex',
versions: [
{ version: 1, type: 'manual', createdAt: '2024-03-20' }
]
}
];
const modalStyle = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'background.paper',
borderRadius: 1,
boxShadow: 24,
p: 4,
};
function FunctionsPage() {
const navigate = useNavigate();
const [expandedFunctions, setExpandedFunctions] = useState({});
const [selectedVersion, setSelectedVersion] = useState(null);
const [modalOpen, setModalOpen] = useState(false);
const toggleFunction = (id) => {
setExpandedFunctions(prev => ({
...prev,
[id]: !prev[id]
}));
};
const openVersionModal = (func, version) => {
setSelectedVersion({ functionId: func.id, functionName: func.name, ...version });
setModalOpen(true);
};
const closeModal = () => {
setModalOpen(false);
};
const handleViewClick = () => {
navigate(`/optimization/${selectedVersion.functionId}/${selectedVersion.version}`);
closeModal();
};
const handleTestClick = () => {
navigate(`/testing/${selectedVersion.functionId}/${selectedVersion.version}`);
closeModal();
};
const getLanguageIcon = (language) => {
switch (language) {
case 'python':
return <CodeIcon fontSize="small" color="primary" />;
case 'nodejs':
return <JavascriptIcon fontSize="small" color="warning" />;
default:
return <CodeIcon fontSize="small" />;
}
};
const getPlatformIcon = (platform) => {
return <CloudIcon fontSize="small" color="info" />;
};
const getVersionTypeChip = (type) => {
if (type === 'auto') {
return <Chip label="Auto" size="small" color="secondary" variant="outlined" sx={{ ml: 1 }} />;
}
return <Chip label="Manual" size="small" color="primary" variant="outlined" sx={{ ml: 1 }} />;
};
return (
<Container maxWidth="lg">
<Box sx={{ my: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
Сохраненные функции
</Typography>
<Paper elevation={2} sx={{ p: 0, mb: 4 }}>
<List sx={{ width: '100%' }}>
{mockFunctions.map((func) => (
<React.Fragment key={func.id}>
<ListItem
secondaryAction={
<IconButton edge="end" onClick={() => toggleFunction(func.id)}>
{expandedFunctions[func.id] ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</IconButton>
}
disablePadding
>
<ListItemButton onClick={() => toggleFunction(func.id)}>
<ListItemIcon>
{getLanguageIcon(func.language)}
</ListItemIcon>
<ListItemText
primary={func.name}
primaryTypographyProps={{ fontWeight: 'medium' }}
/>
<Box sx={{ display: 'flex', alignItems: 'center', mr: 2 }}>
<Tooltip title={func.language === 'python' ? 'Python' : 'Node.js'}>
{getLanguageIcon(func.language)}
</Tooltip>
<Box sx={{ mx: 0.5 }} />
<Tooltip title="Yandex Cloud Functions">
{getPlatformIcon(func.platform)}
</Tooltip>
</Box>
</ListItemButton>
</ListItem>
<Collapse in={expandedFunctions[func.id]} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{func.versions.map((version) => (
<ListItem
key={`${func.id}-v${version.version}`}
sx={{ pl: 4 }}
secondaryAction={
<IconButton edge="end" onClick={() => openVersionModal(func, version)}>
<VisibilityIcon fontSize="small" />
</IconButton>
}
>
<ListItemButton onClick={() => openVersionModal(func, version)}>
<ListItemText
primary={`Версия ${version.version}`}
secondary={version.createdAt}
/>
{getVersionTypeChip(version.type)}
</ListItemButton>
</ListItem>
))}
</List>
</Collapse>
<Divider component="li" />
</React.Fragment>
))}
</List>
</Paper>
<Button
variant="contained"
color="primary"
startIcon={<AddIcon />}
onClick={() => navigate('/optimization')}
sx={{ mb: 4 }}
>
Новая функция
</Button>
<Modal
open={modalOpen}
onClose={closeModal}
aria-labelledby="version-modal-title"
>
<Box sx={modalStyle}>
<Typography id="version-modal-title" variant="h6" component="h2" gutterBottom>
{selectedVersion && `${selectedVersion.functionName} - Версия ${selectedVersion.version}`}
</Typography>
<Typography variant="body2" color="text.secondary" gutterBottom>
{selectedVersion && `Создана: ${selectedVersion.createdAt}`}
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mt: 3 }}>
<Button
variant="outlined"
startIcon={<VisibilityIcon />}
onClick={handleViewClick}
>
Просмотр
</Button>
<Button
variant="contained"
startIcon={<SpeedIcon />}
onClick={handleTestClick}
>
Тестирование
</Button>
</Box>
</Box>
</Modal>
</Box>
</Container>
);
}
export default FunctionsPage;
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment