modified: frontend/src/components/EndpointModal.tsx
This commit is contained in:
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user