modified: frontend/src/components/EndpointModal.tsx

This commit is contained in:
GEgorov
2025-12-15 16:18:51 +03:00
parent bd755cd19f
commit afd79b9c2e

View File

@@ -2,7 +2,7 @@ import { useState } from 'react';
import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query'; import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query';
import { endpointsApi, foldersApi } from '@/services/api'; import { endpointsApi, foldersApi } from '@/services/api';
import { Endpoint, EndpointParameter } from '@/types'; import { Endpoint, EndpointParameter } from '@/types';
import { Plus, Trash2, Play, Edit2 } from 'lucide-react'; import { Plus, Trash2, Play, Edit2, ChevronDown, ChevronUp } from 'lucide-react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import SqlEditor from '@/components/SqlEditor'; import SqlEditor from '@/components/SqlEditor';
import CodeEditor from '@/components/CodeEditor'; import CodeEditor from '@/components/CodeEditor';
@@ -45,6 +45,8 @@ export default function EndpointModal({
const [editingQueryIndex, setEditingQueryIndex] = useState<number | null>(null); const [editingQueryIndex, setEditingQueryIndex] = useState<number | null>(null);
const [showScriptCodeEditor, setShowScriptCodeEditor] = useState(false); const [showScriptCodeEditor, setShowScriptCodeEditor] = useState(false);
const [parametersExpanded, setParametersExpanded] = useState(true);
const [queriesExpanded, setQueriesExpanded] = useState(true);
// Определяем тип выбранной базы данных // Определяем тип выбранной базы данных
const selectedDatabase = databases.find(db => db.id === formData.database_id); const selectedDatabase = databases.find(db => db.id === formData.database_id);
@@ -227,17 +229,29 @@ export default function EndpointModal({
</div> </div>
</div> </div>
<div> <div className="border border-gray-200 rounded-lg">
<div className="flex items-center justify-between mb-2"> <div
<label className="block text-sm font-medium text-gray-700"> className="flex items-center justify-between p-3 bg-gray-50 cursor-pointer hover:bg-gray-100 rounded-t-lg"
Параметры запроса onClick={() => setParametersExpanded(!parametersExpanded)}
<span className="text-xs text-gray-500 ml-2"> >
<div className="flex items-center gap-2">
{parametersExpanded ? <ChevronUp size={18} /> : <ChevronDown size={18} />}
<label className="text-sm font-medium text-gray-700 cursor-pointer">
Параметры запроса
{formData.parameters.length > 0 && (
<span className="ml-2 px-2 py-0.5 bg-primary-100 text-primary-700 rounded-full text-xs">
{formData.parameters.length}
</span>
)}
</label>
<span className="text-xs text-gray-500">
(используйте $имяПараметра в QL запросе) (используйте $имяПараметра в QL запросе)
</span> </span>
</label> </div>
<button <button
type="button" type="button"
onClick={() => { onClick={(e) => {
e.stopPropagation();
const newParam: EndpointParameter = { const newParam: EndpointParameter = {
name: '', name: '',
type: 'string' as const, type: 'string' as const,
@@ -246,100 +260,103 @@ export default function EndpointModal({
description: '', description: '',
}; };
setFormData({ ...formData, parameters: [...formData.parameters, newParam] }); setFormData({ ...formData, parameters: [...formData.parameters, newParam] });
setParametersExpanded(true);
}} }}
className="text-sm text-primary-600 hover:text-primary-700 flex items-center gap-1" className="text-sm text-primary-600 hover:text-primary-700 flex items-center gap-1"
> >
<Plus size={16} /> <Plus size={16} />
Добавить параметр Добавить
</button> </button>
</div> </div>
{formData.parameters.length > 0 ? ( {parametersExpanded && (
<div className="space-y-3 mb-4 border border-gray-200 rounded-lg p-4"> formData.parameters.length > 0 ? (
{formData.parameters.map((param: any, index: number) => ( <div className="space-y-3 p-4">
<div key={index} className="flex gap-2 items-start bg-gray-50 p-3 rounded"> {formData.parameters.map((param: any, index: number) => (
<div className="flex-1 grid grid-cols-5 gap-2"> <div key={index} className="flex gap-2 items-start bg-gray-50 p-3 rounded">
<input <div className="flex-1 grid grid-cols-5 gap-2">
type="text"
placeholder="Имя параметра"
value={param.name}
onChange={(e) => {
const newParams = [...formData.parameters];
newParams[index].name = e.target.value;
setFormData({ ...formData, parameters: newParams });
}}
className="input text-sm"
/>
<select
value={param.type}
onChange={(e) => {
const newParams = [...formData.parameters];
newParams[index].type = e.target.value as 'string' | 'number' | 'boolean' | 'date';
setFormData({ ...formData, parameters: newParams });
}}
className="input text-sm"
>
<option value="string">string</option>
<option value="number">number</option>
<option value="boolean">boolean</option>
<option value="date">date</option>
</select>
<select
value={param.in}
onChange={(e) => {
const newParams = [...formData.parameters];
newParams[index].in = e.target.value as 'query' | 'body' | 'path';
setFormData({ ...formData, parameters: newParams });
}}
className="input text-sm"
>
<option value="query">Query</option>
<option value="body">Body</option>
<option value="path">Path</option>
</select>
<input
type="text"
placeholder="Описание"
value={param.description || ''}
onChange={(e) => {
const newParams = [...formData.parameters];
newParams[index].description = e.target.value;
setFormData({ ...formData, parameters: newParams });
}}
className="input text-sm"
/>
<label className="flex items-center gap-1 text-sm">
<input <input
type="checkbox" type="text"
checked={param.required} placeholder="Имя параметра"
value={param.name}
onChange={(e) => { onChange={(e) => {
const newParams = [...formData.parameters]; const newParams = [...formData.parameters];
newParams[index].required = e.target.checked; newParams[index].name = e.target.value;
setFormData({ ...formData, parameters: newParams }); setFormData({ ...formData, parameters: newParams });
}} }}
className="rounded" className="input text-sm"
/> />
<span className="text-xs">Обязательный</span> <select
</label> value={param.type}
onChange={(e) => {
const newParams = [...formData.parameters];
newParams[index].type = e.target.value as 'string' | 'number' | 'boolean' | 'date';
setFormData({ ...formData, parameters: newParams });
}}
className="input text-sm"
>
<option value="string">string</option>
<option value="number">number</option>
<option value="boolean">boolean</option>
<option value="date">date</option>
</select>
<select
value={param.in}
onChange={(e) => {
const newParams = [...formData.parameters];
newParams[index].in = e.target.value as 'query' | 'body' | 'path';
setFormData({ ...formData, parameters: newParams });
}}
className="input text-sm"
>
<option value="query">Query</option>
<option value="body">Body</option>
<option value="path">Path</option>
</select>
<input
type="text"
placeholder="Описание"
value={param.description || ''}
onChange={(e) => {
const newParams = [...formData.parameters];
newParams[index].description = e.target.value;
setFormData({ ...formData, parameters: newParams });
}}
className="input text-sm"
/>
<label className="flex items-center gap-1 text-sm">
<input
type="checkbox"
checked={param.required}
onChange={(e) => {
const newParams = [...formData.parameters];
newParams[index].required = e.target.checked;
setFormData({ ...formData, parameters: newParams });
}}
className="rounded"
/>
<span className="text-xs">Обязательный</span>
</label>
</div>
<button
type="button"
onClick={() => {
const newParams = formData.parameters.filter((_: any, i: number) => i !== index);
setFormData({ ...formData, parameters: newParams });
}}
className="p-1 hover:bg-red-50 rounded text-red-600"
title="Удалить параметр"
>
<Trash2 size={16} />
</button>
</div> </div>
<button ))}
type="button" </div>
onClick={() => { ) : (
const newParams = formData.parameters.filter((_: any, i: number) => i !== index); <div className="text-center py-4 bg-white rounded-b-lg">
setFormData({ ...formData, parameters: newParams }); <p className="text-sm text-gray-500">Нет параметров. Добавьте параметры для динамического запроса.</p>
}} </div>
className="p-1 hover:bg-red-50 rounded text-red-600" )
title="Удалить параметр"
>
<Trash2 size={16} />
</button>
</div>
))}
</div>
) : (
<div className="text-center py-4 mb-4 border border-gray-200 rounded-lg bg-gray-50">
<p className="text-sm text-gray-500">Нет параметров. Добавьте параметры для динамического запроса.</p>
</div>
)} )}
</div> </div>