feat(tasks): Add quick intent input with Create & Run at top

- Add prominent input box at top of tasks page
- Gradient header with clean design
- HTMX integration for /api/autotask/create
- Show progress indicator during processing
- Display result with app link when done
- Enter key triggers submit
- Auto-refresh task list after creation
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-12-27 23:33:18 -03:00
parent 410f799fb2
commit d96c546c6a
3 changed files with 260 additions and 10 deletions

View file

@ -3,6 +3,173 @@
Automated Intelligent Task Management Interface
============================================================================= */
/* =============================================================================
INTENT INPUT SECTION
============================================================================= */
.intent-input-section {
padding: 24px;
background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%);
border-bottom: 1px solid var(--border);
}
.intent-input-container {
max-width: 900px;
margin: 0 auto;
}
.intent-input-wrapper {
display: flex;
gap: 12px;
align-items: stretch;
}
.quick-intent-input {
flex: 1;
padding: 16px 20px;
font-size: 16px;
border: none;
border-radius: 12px;
background: rgba(255, 255, 255, 0.95);
color: #333;
outline: none;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
transition: box-shadow 0.2s ease;
}
.quick-intent-input::placeholder {
color: #888;
}
.quick-intent-input:focus {
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.25);
}
.btn-create-run {
display: flex;
align-items: center;
gap: 8px;
padding: 16px 28px;
background: #fff;
color: var(--primary);
border: none;
border-radius: 12px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
white-space: nowrap;
}
.btn-create-run:hover {
transform: translateY(-2px);
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.2);
}
.btn-create-run:active {
transform: translateY(0);
}
.btn-create-run .spinner {
display: none;
width: 18px;
height: 18px;
border: 2px solid var(--primary);
border-top-color: transparent;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.btn-create-run.htmx-request .btn-text {
opacity: 0.5;
}
.btn-create-run.htmx-request .spinner {
display: block;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.intent-result {
margin-top: 16px;
padding: 0;
border-radius: 12px;
overflow: hidden;
}
.intent-result:empty {
display: none;
}
.intent-result .result-card {
background: rgba(255, 255, 255, 0.95);
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
.intent-result .result-success {
color: #059669;
}
.intent-result .result-error {
color: #dc2626;
}
.intent-result .result-message {
font-size: 15px;
margin-bottom: 12px;
color: #333;
}
.intent-result .result-link {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 20px;
background: var(--primary);
color: #fff;
text-decoration: none;
border-radius: 8px;
font-weight: 500;
transition: background 0.2s;
}
.intent-result .result-link:hover {
background: #5b21b6;
}
.intent-result .result-progress {
margin-top: 12px;
height: 6px;
background: #e5e7eb;
border-radius: 3px;
overflow: hidden;
}
.intent-result .result-progress-bar {
height: 100%;
background: var(--primary);
border-radius: 3px;
transition: width 0.3s ease;
animation: progress-pulse 1.5s ease-in-out infinite;
}
@keyframes progress-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
/* =============================================================================
LAYOUT STRUCTURE
============================================================================= */

View file

@ -4,9 +4,35 @@
============================================================================= -->
<div class="tasks-app">
<!-- Main Content Area -->
<section class="intent-input-section">
<div class="intent-input-container">
<div class="intent-input-wrapper">
<input
type="text"
id="quick-intent-input"
name="intent"
class="quick-intent-input"
placeholder="What would you like to do? e.g., 'create a CRM app' or 'remind me to call John tomorrow'"
autocomplete="off"
/>
<button
id="quick-intent-btn"
class="btn-create-run"
hx-post="/api/autotask/create"
hx-include="#quick-intent-input"
hx-target="#intent-result"
hx-swap="innerHTML"
hx-indicator="#intent-spinner"
>
<span class="btn-text">Create & Run</span>
<span class="spinner" id="intent-spinner"></span>
</button>
</div>
<div id="intent-result" class="intent-result"></div>
</div>
</section>
<main class="tasks-main">
<!-- Task List Panel (Left) -->
<section class="tasks-list-panel">
<div class="tasks-list-header">
<div class="tasks-list-title">

View file

@ -28,21 +28,78 @@ document.addEventListener("DOMContentLoaded", function () {
});
function initTasksApp() {
// Initialize WebSocket for real-time updates
initWebSocket();
// Setup event listeners
setupEventListeners();
// Setup keyboard shortcuts
setupKeyboardShortcuts();
// Auto-scroll agent log to bottom
setupIntentInputHandlers();
scrollAgentLogToBottom();
console.log("[Tasks] Initialized");
}
function setupIntentInputHandlers() {
const input = document.getElementById("quick-intent-input");
const btn = document.getElementById("quick-intent-btn");
if (input) {
input.addEventListener("keypress", function (e) {
if (e.key === "Enter" && input.value.trim()) {
btn.click();
}
});
}
document.body.addEventListener("htmx:beforeRequest", function (e) {
if (e.detail.elt.id === "quick-intent-btn") {
const resultDiv = document.getElementById("intent-result");
resultDiv.innerHTML = `
<div class="result-card">
<div class="result-message">Processing your request...</div>
<div class="result-progress">
<div class="result-progress-bar" style="width: 30%"></div>
</div>
</div>
`;
}
});
document.body.addEventListener("htmx:afterRequest", function (e) {
if (e.detail.elt.id === "quick-intent-btn") {
const resultDiv = document.getElementById("intent-result");
try {
const response = JSON.parse(e.detail.xhr.responseText);
if (response.success) {
let html = `<div class="result-card">
<div class="result-message result-success"> ${response.message || "Done!"}</div>`;
if (response.app_url) {
html += `<a href="${response.app_url}" class="result-link" target="_blank">
Open App
</a>`;
}
if (response.task_id) {
html += `<div style="margin-top:8px;color:#666;font-size:13px;">Task ID: ${response.task_id}</div>`;
}
html += `</div>`;
resultDiv.innerHTML = html;
document.getElementById("quick-intent-input").value = "";
htmx.trigger(document.body, "taskCreated");
} else {
resultDiv.innerHTML = `<div class="result-card">
<div class="result-message result-error"> ${response.error || response.message || "Something went wrong"}</div>
</div>`;
}
} catch (err) {
resultDiv.innerHTML = `<div class="result-card">
<div class="result-message result-error"> Failed to process response</div>
</div>`;
}
}
});
}
// =============================================================================
// WEBSOCKET CONNECTION
// =============================================================================