1 <!DOCTYPE html>2 <html lang="es">3 <head>4 <meta charset="UTF-8" />5 <meta name="viewport" content="width=device-width, initial-scale=1.0"/>6 <title>FlowTest Console</title>7 <script src="https://cdn.tailwindcss.com"></script>8 </head>9 <body class="h-screen flex overflow-hidden bg-gray-100">10 11 <!-- Sidebar -->12 <aside class="w-64 bg-white border-r overflow-y-auto">13 <div class="p-4 text-xl font-semibold">Steps</div>14 <nav class="space-y-1">15 <!-- Repite este button por cada paso -->16 <button 17 class="w-full text-left px-4 py-2 hover:bg-gray-50 focus:bg-gray-100"18 onclick="selectStep('step1')"19 id="btn-step1"20 >21 Paso 1: Ping the site22 </button>23 <!-- + botón añadir -->24 <button 25 class="w-full text-left px-4 py-2 text-blue-600 hover:bg-blue-50"26 onclick="addStep()"27 >28 + Añadir paso29 </button>30 </nav>31 </aside>32 33 <!-- Main content -->34 <div class="flex-1 flex flex-col">35 <!-- Tabs Visual / JSON -->36 <div class="border-b bg-white">37 <nav class="flex">38 <button39 class="px-6 py-3 -mb-px font-medium border-b-2 border-blue-600 text-blue-600"40 onclick="openTab('visual')"41 id="tab-btn-visual"42 >43 Visual44 </button>45 <button46 class="px-6 py-3 -mb-px font-medium border-b-2 border-transparent text-gray-600 hover:text-blue-600"47 onclick="openTab('json')"48 id="tab-btn-json"49 >50 JSON51 </button>52 </nav>53 </div>54 <!-- Tab panels -->55 <div class="flex-1 overflow-auto p-6">56 <!-- Visual -->57 <div id="tab-visual" class="">58 <!-- Paso 1 acordeón -->59 <section class="mb-6 bg-white shadow rounded-lg overflow-hidden">60 <header 61 class="flex items-center justify-between px-6 py-4 cursor-pointer"62 onclick="toggleAccordion('panel-step1')"63 >64 <h2 class="text-lg font-semibold">Paso 1: Ping the site</h2>65 <svg 66 id="icon-panel-step1"67 class="w-5 h-5 transform transition-transform"68 fill="none" stroke="currentColor" viewBox="0 0 24 24"69 >70 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"71 d="M19 9l-7 7-7-7"/>72 </svg>73 </header>74 <div id="panel-step1" class="px-6 pb-6 hidden">75 <!-- Grid 2 columnas -->76 <div class="grid grid-cols-1 md:grid-cols-2 gap-6">77 <!-- Columna izquierda: Request -->78 <div class="space-y-4">79 <div>80 <label class="block text-sm font-medium">Método</label>81 <select class="mt-1 block w-full border-gray-300 rounded shadow-sm p-2">82 <option>GET</option>83 <option selected>POST</option>84 <option>PUT</option>85 <option>DELETE</option>86 </select>87 </div>88 <div>89 <label class="block text-sm font-medium">URL</label>90 <input91 type="text"92 class="mt-1 block w-full border-gray-300 rounded shadow-sm p-2"93 value="{{ base }}/api/v1/example"94 />95 </div>96 <div>97 <label class="block text-sm font-medium">Body (JSON)</label>98 <textarea99 class="mt-1 block w-full border-gray-300 rounded shadow-sm p-2 h-32 font-mono text-sm"100 >{101 "name": "My name"102 }</textarea>103 </div>104 </div>105 106 <!-- Columna derecha: Avanzado -->107 <div class="space-y-4">108 <!-- Headers -->109 <div>110 <div class="flex justify-between items-center mb-1">111 <h3 class="text-sm font-medium">Headers</h3>112 <button class="text-green-600 text-lg leading-none" title="Añadir header">+</button>113 </div>114 <!-- Ejemplo fila header -->115 <div class="flex space-x-2">116 <input117 placeholder="Key"118 class="flex-1 border-gray-300 rounded shadow-sm p-2 text-sm"119 />120 <input121 placeholder="Value"122 class="flex-1 border-gray-300 rounded shadow-sm p-2 text-sm"123 />124 <button class="text-red-500" title="Eliminar">×</button>125 </div>126 </div>127 <!-- Assign -->128 <div>129 <div class="flex justify-between items-center mb-1">130 <h3 class="text-sm font-medium">Asignaciones</h3>131 <button class="text-green-600 text-lg leading-none" title="Añadir asignación">+</button>132 </div>133 <!-- Fila asignación -->134 <div class="flex space-x-2">135 <input136 placeholder="Variable"137 class="flex-1 border-gray-300 rounded shadow-sm p-2 text-sm"138 />139 <input140 placeholder="Expresión"141 class="flex-1 border-gray-300 rounded shadow-sm p-2 text-sm"142 />143 <button class="text-red-500" title="Eliminar">×</button>144 </div>145 </div>146 <!-- Assertions -->147 <div>148 <div class="flex justify-between items-center mb-1">149 <h3 class="text-sm font-medium">Aserciones</h3>150 <button class="text-green-600 text-lg leading-none" title="Añadir aserción">+</button>151 </div>152 <!-- Fila aserción -->153 <div class="flex space-x-2">154 <select class="border-gray-300 rounded shadow-sm p-2 text-sm">155 <option>response.status</option>156 <option>response.body.name</option>157 <!-- ... -->158 </select>159 <select class="border-gray-300 rounded shadow-sm p-2 text-sm">160 <option>eq</option>161 <option>neq</option>162 <option>contains</option>163 </select>164 <input165 placeholder="Valor"166 class="flex-1 border-gray-300 rounded shadow-sm p-2 text-sm"167 />168 <button class="text-red-500" title="Eliminar">×</button>169 </div>170 </div>171 <!-- Retry -->172 <div>173 <div class="flex justify-between items-center mb-1">174 <h3 class="text-sm font-medium">Retry</h3>175 <button class="text-green-600 text-lg leading-none" title="Añadir retry">+</button>176 </div>177 <!-- Fila retry -->178 <div class="flex space-x-2">179 <input180 type="number"181 placeholder="Attempts"182 class="w-20 border-gray-300 rounded shadow-sm p-2 text-sm"183 />184 <input185 type="number"186 placeholder="Sleep ms"187 class="w-24 border-gray-300 rounded shadow-sm p-2 text-sm"188 />189 <button class="text-red-500" title="Eliminar">×</button>190 </div>191 </div>192 </div>193 </div>194 </div>195 </section>196 197 <!-- Aquí podrías clonar la sección de paso para Paso 2, Paso 3… -->198 </div>199 200 <!-- JSON -->201 <div id="tab-json" class="hidden">202 <textarea203 class="w-full h-full font-mono text-sm border-gray-300 rounded shadow-sm p-4"204 >{205 "variables": [206 { "base": "https://flowtest.io" }207 ],208 "steps": [209 {210 "name": "Ping the site",211 "method": "POST",212 "url": "{{ base }}/api/v1/example",213 "headers": [214 { "Content-Type": "application/json" }215 ],216 "body": { "name": "My name" },217 "assign": [218 { "id": "response.body.id" }219 ],220 "assert": [221 { "left": "response.status", "op": "eq", "right": 201 },222 { "left": "response.body.name", "op": "eq", "right": "My name" }223 ]224 }225 ]226 }</textarea>227 </div>228 </div>229 </div>230 231 <!-- Scripts para tabs y acordeones -->232 <script>233 function toggleAccordion(id) {234 const panel = document.getElementById(id);235 const icon = document.getElementById('icon-' + id);236 panel.classList.toggle('hidden');237 icon.classList.toggle('rotate-180');238 }239 240 function openTab(name) {241 document.querySelectorAll('[id^="tab-"]').forEach(el => el.classList.add('hidden'));242 document.getElementById('tab-' + name).classList.remove('hidden');243 document.querySelectorAll('[id^="tab-btn-"]').forEach(btn => {244 btn.classList.remove('border-blue-600','text-blue-600');245 btn.classList.add('border-transparent','text-gray-600');246 });247 const btn = document.getElementById('tab-btn-' + name);248 btn.classList.add('border-blue-600','text-blue-600');249 btn.classList.remove('border-transparent','text-gray-600');250 }251 252 function selectStep(stepId) {253 // lógica para resaltar el paso en sidebar y mostrar su acordeón254 // (puedes adaptar según tu data-model)255 toggleAccordion('panel-' + stepId);256 }257 258 function addStep() {259 alert('Aquí iría la lógica para añadir dinámicamente un nuevo paso');260 }261 </script>262 263 </body>264 </html>265 Enlace
El enlace para compartir es:

