2025-12-15 16:33:23 -03:00
|
|
|
|
<!-- =============================================================================
|
2025-12-27 22:38:37 -03:00
|
|
|
|
TASKS APP - Autonomous Task Management
|
|
|
|
|
|
Bot Database Architecture with Shared Resources
|
2025-12-15 16:33:23 -03:00
|
|
|
|
============================================================================= -->
|
|
|
|
|
|
|
2025-12-15 18:48:40 -03:00
|
|
|
|
<div class="tasks-app">
|
2025-12-27 23:33:18 -03:00
|
|
|
|
<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>
|
|
|
|
|
|
|
2025-12-15 16:33:23 -03:00
|
|
|
|
<main class="tasks-main">
|
|
|
|
|
|
<section class="tasks-list-panel">
|
|
|
|
|
|
<div class="tasks-list-header">
|
|
|
|
|
|
<div class="tasks-list-title">
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<h1>Autonomous Tasks</h1>
|
|
|
|
|
|
<span
|
|
|
|
|
|
class="tasks-count"
|
|
|
|
|
|
id="tasks-total-count"
|
|
|
|
|
|
hx-get="/api/tasks/stats"
|
|
|
|
|
|
hx-trigger="load, taskCreated from:body"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>Loading...</span
|
|
|
|
|
|
></h1>
|
2025-12-15 23:16:09 -03:00
|
|
|
|
<button
|
|
|
|
|
|
class="btn-new-intent"
|
|
|
|
|
|
onclick="showNewIntentModal()"
|
|
|
|
|
|
title="Create new intent"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg
|
|
|
|
|
|
width="16"
|
|
|
|
|
|
height="16"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
>
|
|
|
|
|
|
<line x1="12" y1="5" x2="12" y2="19" />
|
|
|
|
|
|
<line x1="5" y1="12" x2="19" y2="12" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
New Intent
|
|
|
|
|
|
</button>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Status Filter Pills -->
|
|
|
|
|
|
<div class="status-filters" id="status-filters">
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="status-pill complete active"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
data-filter="all"
|
|
|
|
|
|
hx-get="/api/tasks?filter=all"
|
2025-12-15 16:33:23 -03:00
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
All <span class="pill-count" id="count-all">-</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="status-pill active-intents"
|
|
|
|
|
|
data-filter="active"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
hx-get="/api/tasks?filter=active"
|
2025-12-15 16:33:23 -03:00
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
Active
|
|
|
|
|
|
<span class="pill-count" id="count-active">-</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
2025-12-27 22:38:37 -03:00
|
|
|
|
class="status-pill complete"
|
|
|
|
|
|
data-filter="completed"
|
|
|
|
|
|
hx-get="/api/tasks?filter=completed"
|
2025-12-15 16:33:23 -03:00
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
Completed
|
|
|
|
|
|
<span class="pill-count" id="count-completed">-</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
2025-12-27 22:38:37 -03:00
|
|
|
|
class="status-pill blocked"
|
|
|
|
|
|
data-filter="priority"
|
|
|
|
|
|
hx-get="/api/tasks?filter=priority"
|
2025-12-15 16:33:23 -03:00
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
High Priority
|
|
|
|
|
|
<span class="pill-count" id="count-priority">-</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
2025-12-27 22:38:37 -03:00
|
|
|
|
class="status-pill pending-info"
|
|
|
|
|
|
data-filter="pending"
|
|
|
|
|
|
hx-get="/api/pending-info"
|
|
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
|
|
|
|
|
🔑 Pending Info
|
|
|
|
|
|
<span class="pill-count" id="count-pending">-</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="status-pill goals"
|
|
|
|
|
|
data-filter="goals"
|
|
|
|
|
|
hx-get="/api/goals"
|
2025-12-15 16:33:23 -03:00
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
🎯 Goals
|
|
|
|
|
|
<span class="pill-count" id="count-goals">-</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="status-pill schedulers"
|
|
|
|
|
|
data-filter="schedulers"
|
|
|
|
|
|
hx-get="/api/schedulers"
|
|
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
|
|
|
|
|
⏰ Schedulers
|
|
|
|
|
|
<span class="pill-count" id="count-schedulers">-</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="status-pill monitors"
|
|
|
|
|
|
data-filter="monitors"
|
|
|
|
|
|
hx-get="/api/monitors"
|
|
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
|
|
|
|
|
👁️ Monitors
|
|
|
|
|
|
<span class="pill-count" id="count-monitors">-</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="status-pill tables"
|
|
|
|
|
|
data-filter="tables"
|
|
|
|
|
|
hx-get="/api/tables"
|
|
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
|
|
|
|
|
🗃️ Tables
|
|
|
|
|
|
<span class="pill-count" id="count-tables">-</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="status-pill apps"
|
|
|
|
|
|
data-filter="apps"
|
|
|
|
|
|
hx-get="/api/apps"
|
|
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
|
|
|
|
|
📱 Apps
|
|
|
|
|
|
<span class="pill-count" id="count-apps">-</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Task List -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="tasks-list-scroll"
|
|
|
|
|
|
id="task-list"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
hx-get="/api/tasks?filter=all"
|
|
|
|
|
|
hx-trigger="load, taskCreated from:body"
|
2025-12-03 18:42:22 -03:00
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<!-- Loading indicator - will be replaced by HTMX -->
|
|
|
|
|
|
<div class="loading-state">
|
|
|
|
|
|
<div class="loading-spinner"></div>
|
|
|
|
|
|
<p>Loading tasks...</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Task Detail Panel (Right) -->
|
|
|
|
|
|
<aside class="task-detail-panel" id="task-detail-panel">
|
|
|
|
|
|
<!-- Empty state shown when no task selected -->
|
|
|
|
|
|
<div class="task-detail-empty" id="task-detail-empty">
|
|
|
|
|
|
<div class="empty-state">
|
|
|
|
|
|
<svg
|
|
|
|
|
|
width="64"
|
|
|
|
|
|
height="64"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="1.5"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path
|
|
|
|
|
|
d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<rect x="9" y="3" width="6" height="4" rx="2" />
|
|
|
|
|
|
<path d="M9 12h6" />
|
|
|
|
|
|
<path d="M9 16h6" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
<h3>Select a task</h3>
|
|
|
|
|
|
<p>Click on a task from the list to view details</p>
|
|
|
|
|
|
<div class="empty-state-info">
|
|
|
|
|
|
<p class="info-text">
|
|
|
|
|
|
<strong>Bot Database:</strong> All apps share the same database tables.<br>
|
|
|
|
|
|
<strong>Shared Resources:</strong> Schedulers, tools, and monitors work across all apps.
|
|
|
|
|
|
</p>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Dynamic task detail content loaded via HTMX -->
|
|
|
|
|
|
<div id="task-detail-content" style="display: none">
|
|
|
|
|
|
<div class="task-detail-header">
|
|
|
|
|
|
<div class="task-detail-title-row">
|
|
|
|
|
|
<h2 class="task-detail-title" id="detail-title">-</h2>
|
|
|
|
|
|
<div class="task-detail-actions">
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="task-detail-action-btn"
|
|
|
|
|
|
title="Edit"
|
|
|
|
|
|
onclick="editSelectedTask()"
|
|
|
|
|
|
>
|
|
|
|
|
|
✏️
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="task-detail-action-btn"
|
|
|
|
|
|
title="Delete"
|
|
|
|
|
|
onclick="deleteSelectedTask()"
|
|
|
|
|
|
>
|
|
|
|
|
|
🗑️
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button class="task-detail-action-btn" title="More">
|
|
|
|
|
|
⋯
|
|
|
|
|
|
</button>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="task-detail-meta">
|
|
|
|
|
|
<span class="task-detail-meta-item" id="detail-status">
|
|
|
|
|
|
<span class="icon">📊</span>
|
|
|
|
|
|
<span id="detail-status-text">-</span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span
|
|
|
|
|
|
class="task-detail-meta-item"
|
|
|
|
|
|
id="detail-priority"
|
2025-12-15 16:33:23 -03:00
|
|
|
|
>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<span class="icon">🎯</span>
|
|
|
|
|
|
<span id="detail-priority-text">-</span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span
|
|
|
|
|
|
class="task-detail-meta-item"
|
|
|
|
|
|
id="detail-due-date"
|
|
|
|
|
|
>
|
|
|
|
|
|
<span class="icon">📅</span>
|
|
|
|
|
|
<span id="detail-due-date-text">-</span>
|
|
|
|
|
|
</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
</div>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="task-detail-scroll">
|
|
|
|
|
|
<!-- Task Description Section -->
|
|
|
|
|
|
<section class="task-description-section">
|
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
|
<span class="section-title">Description</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<p class="task-description" id="detail-description">
|
|
|
|
|
|
No description provided.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Task Actions Section -->
|
|
|
|
|
|
<section class="task-actions-section">
|
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
|
<span class="section-title">Quick Actions</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="task-action-buttons">
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="task-action-btn"
|
|
|
|
|
|
onclick="updateTaskStatus('in_progress')"
|
|
|
|
|
|
>
|
|
|
|
|
|
▶️ Start
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="task-action-btn"
|
|
|
|
|
|
onclick="updateTaskStatus('done')"
|
|
|
|
|
|
>
|
|
|
|
|
|
✓ Complete
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="task-action-btn"
|
|
|
|
|
|
onclick="updateTaskStatus('blocked')"
|
|
|
|
|
|
>
|
|
|
|
|
|
⏸️ Block
|
|
|
|
|
|
</button>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
</section>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<!-- Task Activity Log -->
|
|
|
|
|
|
<section class="progress-log-section">
|
|
|
|
|
|
<div class="progress-log-header">
|
|
|
|
|
|
<span class="progress-log-title">Activity</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="step-list" id="detail-activity">
|
|
|
|
|
|
<div class="step-item completed">
|
|
|
|
|
|
<div class="step-icon">✓</div>
|
|
|
|
|
|
<div class="step-content">
|
|
|
|
|
|
<div class="step-name">Task created</div>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="step-detail"
|
|
|
|
|
|
id="detail-created-at"
|
|
|
|
|
|
>
|
|
|
|
|
|
-
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
</section>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<!-- Goal Progress (shown for GOAL type) -->
|
|
|
|
|
|
<section
|
|
|
|
|
|
class="goal-progress-section"
|
|
|
|
|
|
id="goal-progress-section"
|
|
|
|
|
|
style="display: none"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
|
<span class="section-title">🎯 Goal Progress</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="goal-progress-content">
|
|
|
|
|
|
<div class="goal-progress-bar">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="goal-progress-fill"
|
|
|
|
|
|
id="goal-progress-fill"
|
|
|
|
|
|
style="width: 0%"
|
|
|
|
|
|
></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="goal-progress-stats">
|
|
|
|
|
|
<span id="goal-current-value">0</span> /
|
|
|
|
|
|
<span id="goal-target-value">0</span> (<span
|
|
|
|
|
|
id="goal-percent"
|
|
|
|
|
|
>0</span
|
|
|
|
|
|
>%)
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="goal-last-action" id="goal-last-action">
|
|
|
|
|
|
Last action: -
|
|
|
|
|
|
</div>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
</section>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<!-- Pending Info Fill Form (shown for PENDING type) -->
|
|
|
|
|
|
<section
|
|
|
|
|
|
class="pending-fill-section"
|
|
|
|
|
|
id="pending-fill-section"
|
|
|
|
|
|
style="display: none"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
|
<span class="section-title"
|
|
|
|
|
|
>🔑 Fill Information</span
|
|
|
|
|
|
>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="pending-fill-content">
|
|
|
|
|
|
<p class="pending-reason" id="pending-reason">-</p>
|
|
|
|
|
|
<form
|
|
|
|
|
|
id="pending-fill-form"
|
|
|
|
|
|
hx-post="/api/pending-info/fill"
|
|
|
|
|
|
hx-swap="none"
|
|
|
|
|
|
>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="hidden"
|
|
|
|
|
|
name="id"
|
|
|
|
|
|
id="pending-fill-id"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label id="pending-fill-label">Value</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
class="form-input"
|
|
|
|
|
|
name="value"
|
|
|
|
|
|
id="pending-fill-value"
|
|
|
|
|
|
required
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<button type="submit" class="task-action-btn">
|
|
|
|
|
|
Save to Bot Config
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</form>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
</section>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<!-- Scheduler Info (shown for SCHEDULER type) -->
|
|
|
|
|
|
<section
|
|
|
|
|
|
class="scheduler-info-section"
|
|
|
|
|
|
id="scheduler-info-section"
|
|
|
|
|
|
style="display: none"
|
2025-12-15 16:33:23 -03:00
|
|
|
|
>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="section-header">
|
|
|
|
|
|
<span class="section-title">⏰ Schedule (Shared)</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="scheduler-info-content">
|
|
|
|
|
|
<div class="scheduler-cron" id="scheduler-cron">
|
|
|
|
|
|
Cron: -
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="scheduler-next" id="scheduler-next">
|
|
|
|
|
|
Next run: -
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="scheduler-file" id="scheduler-file">
|
|
|
|
|
|
File: .gbdialog/schedulers/-
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<p class="shared-resource-note">
|
|
|
|
|
|
ℹ️ This scheduler runs for all apps in this bot
|
|
|
|
|
|
</p>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
</section>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<!-- Monitor Info (shown for MONITOR type) -->
|
|
|
|
|
|
<section
|
|
|
|
|
|
class="monitor-info-section"
|
|
|
|
|
|
id="monitor-info-section"
|
|
|
|
|
|
style="display: none"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
|
<span class="section-title">👁️ Monitor (Shared)</span>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="monitor-info-content">
|
|
|
|
|
|
<div class="monitor-target" id="monitor-target">
|
|
|
|
|
|
Target: -
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div class="monitor-interval" id="monitor-interval">
|
|
|
|
|
|
Interval: -
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div
|
|
|
|
|
|
class="monitor-last-check"
|
|
|
|
|
|
id="monitor-last-check"
|
|
|
|
|
|
>
|
|
|
|
|
|
Last check: -
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<div
|
|
|
|
|
|
class="monitor-last-value"
|
|
|
|
|
|
id="monitor-last-value"
|
|
|
|
|
|
>
|
|
|
|
|
|
Last value: -
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<p class="shared-resource-note">
|
|
|
|
|
|
ℹ️ This monitor triggers for all apps in this bot
|
|
|
|
|
|
</p>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Table Info (shown for TABLE type) -->
|
|
|
|
|
|
<section
|
|
|
|
|
|
class="table-info-section"
|
|
|
|
|
|
id="table-info-section"
|
|
|
|
|
|
style="display: none"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
|
<span class="section-title">🗃️ Table (Shared)</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="table-info-content">
|
|
|
|
|
|
<div class="table-name" id="table-name">
|
|
|
|
|
|
Table: -
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="table-fields" id="table-fields">
|
|
|
|
|
|
Fields: -
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="table-records" id="table-records">
|
|
|
|
|
|
Records: -
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="table-source">
|
|
|
|
|
|
Defined in: <code>.gbdialog/tables.bas</code>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="shared-resource-note">
|
|
|
|
|
|
ℹ️ All apps in this bot access this table via FIND keyword
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div class="table-query-example">
|
|
|
|
|
|
<strong>Example:</strong>
|
|
|
|
|
|
<code id="table-query-example">FIND * IN table_name</code>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
</section>
|
2025-12-03 18:42:22 -03:00
|
|
|
|
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<!-- App Info (shown for APP type) -->
|
|
|
|
|
|
<section
|
|
|
|
|
|
class="app-info-section"
|
|
|
|
|
|
id="app-info-section"
|
|
|
|
|
|
style="display: none"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
|
<span class="section-title">📱 App</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="app-info-content">
|
|
|
|
|
|
<div class="app-name" id="app-name">
|
|
|
|
|
|
App: -
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="app-url" id="app-url">
|
|
|
|
|
|
URL: /apps/-
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="app-tables" id="app-tables">
|
|
|
|
|
|
Uses tables: -
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="shared-resource-note">
|
|
|
|
|
|
ℹ️ This app uses the bot's shared database
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div class="app-actions">
|
|
|
|
|
|
<button class="task-action-btn" onclick="openApp()">
|
|
|
|
|
|
🚀 Open App
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button class="task-action-btn" onclick="editAppTables()">
|
|
|
|
|
|
🗃️ Edit Tables
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</div>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
</div>
|
2025-12-15 16:33:23 -03:00
|
|
|
|
</aside>
|
|
|
|
|
|
</main>
|
|
|
|
|
|
</div>
|
2025-12-03 18:42:22 -03:00
|
|
|
|
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<!-- Bot Database Summary Panel -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="bot-db-summary-panel"
|
|
|
|
|
|
id="bot-db-summary-panel"
|
|
|
|
|
|
hx-get="/api/bot/database/summary"
|
|
|
|
|
|
hx-trigger="load"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="bot-db-loading">Loading bot database info...</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Pending Info Summary Panel -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="pending-summary-panel"
|
|
|
|
|
|
id="pending-summary-panel"
|
|
|
|
|
|
hx-get="/api/pending-info/summary"
|
|
|
|
|
|
hx-trigger="load"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
></div>
|
|
|
|
|
|
|
2025-12-15 23:16:09 -03:00
|
|
|
|
<!-- New Intent Modal -->
|
|
|
|
|
|
<div class="modal-overlay" id="newIntentModal">
|
|
|
|
|
|
<div class="modal">
|
|
|
|
|
|
<div class="modal-header">
|
|
|
|
|
|
<h3>Create New Intent</h3>
|
|
|
|
|
|
<button class="modal-close" onclick="closeNewIntentModal()">
|
|
|
|
|
|
<svg
|
|
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
>
|
|
|
|
|
|
<line x1="18" y1="6" x2="6" y2="18" />
|
|
|
|
|
|
<line x1="6" y1="6" x2="18" y2="18" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="modal-body">
|
|
|
|
|
|
<form
|
|
|
|
|
|
id="newIntentForm"
|
|
|
|
|
|
hx-post="/api/tasks"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
hx-ext="json-enc"
|
2025-12-15 23:16:09 -03:00
|
|
|
|
hx-target="#task-list"
|
|
|
|
|
|
hx-swap="afterbegin"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
hx-on::after-request="if(event.detail.successful) { closeNewIntentModal(); htmx.trigger(document.body, 'taskCreated'); }"
|
2025-12-15 23:16:09 -03:00
|
|
|
|
>
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label" for="intentTitle"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
>Intent (Natural Language)</label
|
2025-12-15 23:16:09 -03:00
|
|
|
|
>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
class="form-input"
|
|
|
|
|
|
id="intentTitle"
|
|
|
|
|
|
name="title"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
placeholder="e.g., cria app de agendamento, quando IBM mudar avisa, todo dia 9h resumo..."
|
2025-12-15 23:16:09 -03:00
|
|
|
|
required
|
|
|
|
|
|
/>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<p class="form-hint">
|
|
|
|
|
|
Describe what you want: create apps, schedule tasks, monitor changes, or set goals.
|
|
|
|
|
|
</p>
|
2025-12-15 23:16:09 -03:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label" for="intentDescription"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
>Additional Details (Optional)</label
|
2025-12-15 23:16:09 -03:00
|
|
|
|
>
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
class="form-input"
|
|
|
|
|
|
id="intentDescription"
|
|
|
|
|
|
name="description"
|
|
|
|
|
|
rows="3"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
placeholder="Add more context: tables needed, fields, triggers, etc."
|
2025-12-15 23:16:09 -03:00
|
|
|
|
></textarea>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="form-row">
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label" for="intentDueDate"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
>Due Date (Optional)</label
|
2025-12-15 23:16:09 -03:00
|
|
|
|
>
|
|
|
|
|
|
<input
|
2025-12-27 22:38:37 -03:00
|
|
|
|
type="datetime-local"
|
2025-12-15 23:16:09 -03:00
|
|
|
|
class="form-input"
|
|
|
|
|
|
id="intentDueDate"
|
|
|
|
|
|
name="due_date"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label" for="intentPriority"
|
|
|
|
|
|
>Priority</label
|
|
|
|
|
|
>
|
|
|
|
|
|
<select
|
|
|
|
|
|
class="form-input"
|
|
|
|
|
|
id="intentPriority"
|
|
|
|
|
|
name="priority"
|
|
|
|
|
|
>
|
|
|
|
|
|
<option value="normal">Normal</option>
|
|
|
|
|
|
<option value="high">High</option>
|
|
|
|
|
|
<option value="urgent">Urgent</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="form-group">
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<label class="form-label" for="intentType"
|
|
|
|
|
|
>Intent Type (Auto-detected)</label
|
2025-12-15 23:16:09 -03:00
|
|
|
|
>
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<select
|
2025-12-15 23:16:09 -03:00
|
|
|
|
class="form-input"
|
2025-12-27 22:38:37 -03:00
|
|
|
|
id="intentType"
|
|
|
|
|
|
name="intent_type"
|
|
|
|
|
|
>
|
|
|
|
|
|
<option value="auto">Auto-detect</option>
|
|
|
|
|
|
<option value="app">📱 App - Create HTMX app</option>
|
|
|
|
|
|
<option value="table">🗃️ Table - Define new table</option>
|
|
|
|
|
|
<option value="todo">✅ Todo - Simple task</option>
|
|
|
|
|
|
<option value="goal">🎯 Goal - Autonomous goal</option>
|
|
|
|
|
|
<option value="schedule">⏰ Schedule - Recurring task</option>
|
|
|
|
|
|
<option value="monitor">👁️ Monitor - Watch for changes</option>
|
|
|
|
|
|
<option value="tool">🔧 Tool - Voice/chat command</option>
|
|
|
|
|
|
<option value="action">⚡ Action - Execute immediately</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="form-info-box">
|
|
|
|
|
|
<strong>🗄️ Bot Database Architecture:</strong>
|
|
|
|
|
|
<ul>
|
|
|
|
|
|
<li>All apps share the same database tables</li>
|
|
|
|
|
|
<li>Tables are defined in <code>.gbdialog/tables.bas</code></li>
|
|
|
|
|
|
<li>Schedulers, tools, and monitors are shared resources</li>
|
|
|
|
|
|
<li>Use <code>FIND x IN table</code> to query data</li>
|
|
|
|
|
|
</ul>
|
2025-12-15 23:16:09 -03:00
|
|
|
|
</div>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="modal-footer">
|
|
|
|
|
|
<button
|
2025-12-27 22:38:37 -03:00
|
|
|
|
class="decision-btn decision-btn-secondary</strong>"
|
2025-12-15 23:16:09 -03:00
|
|
|
|
onclick="closeNewIntentModal()"
|
|
|
|
|
|
>
|
|
|
|
|
|
Cancel
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="decision-btn decision-btn-primary"
|
|
|
|
|
|
type="submit"
|
|
|
|
|
|
form="newIntentForm"
|
|
|
|
|
|
>
|
|
|
|
|
|
Create Intent
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-12-27 22:38:37 -03:00
|
|
|
|
<!-- Edit Tables Modal -->
|
|
|
|
|
|
<div class="modal-overlay" id="editTablesModal">
|
|
|
|
|
|
<div class="modal modal-large">
|
|
|
|
|
|
<div class="modal-header">
|
|
|
|
|
|
<h3>🗃️ Edit Tables (tables.bas)</h3>
|
|
|
|
|
|
<button class="modal-close" onclick="closeEditTablesModal()">
|
|
|
|
|
|
<svg
|
|
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
>
|
|
|
|
|
|
<line x1="18" y1="6" x2="6" y2="18" />
|
|
|
|
|
|
<line x1="6" y1="6" x2="18" y2="18" /></h3>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="modal-body">
|
|
|
|
|
|
<p class="modal-description">
|
|
|
|
|
|
Define tables using the TABLE keyword. Changes auto-sync to the bot database.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div
|
|
|
|
|
|
id="tables-editor"
|
|
|
|
|
|
hx-get="/api/tables/source"
|
|
|
|
|
|
hx-trigger="load"
|
|
|
|
|
|
hx-swap="innerHTML"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="loading-state">Loading tables.bas...</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="modal-footer">
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="decision-btn decision-btn-secondary"
|
|
|
|
|
|
onclick="closeEditTablesModal()"
|
|
|
|
|
|
>
|
|
|
|
|
|
Cancel
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="decision-btn decision-btn-primary"
|
|
|
|
|
|
onclick="saveTablesFile()"
|
|
|
|
|
|
>
|
|
|
|
|
|
Save & Sync
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-12-15 23:16:09 -03:00
|
|
|
|
<script>
|
|
|
|
|
|
// Conditionally load tasks.js only if not already loaded
|
|
|
|
|
|
if (typeof TasksState === "undefined") {
|
|
|
|
|
|
const script = document.createElement("script");
|
|
|
|
|
|
script.src = "tasks/tasks.js";
|
|
|
|
|
|
document.head.appendChild(script);
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
<script>
|
|
|
|
|
|
// New Intent Modal functions
|
|
|
|
|
|
function showNewIntentModal() {
|
|
|
|
|
|
document.getElementById("newIntentModal").classList.add("show");
|
|
|
|
|
|
document.getElementById("intentTitle").focus();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function closeNewIntentModal() {
|
|
|
|
|
|
document.getElementById("newIntentModal").classList.remove("show");
|
|
|
|
|
|
document.getElementById("newIntentForm").reset();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-27 22:38:37 -03:00
|
|
|
|
// Edit Tables Modal functions
|
|
|
|
|
|
function showEditTablesModal() {
|
|
|
|
|
|
document.getElementById("editTablesModal").classList.add("show");
|
|
|
|
|
|
htmx.trigger(document.getElementById("tables-editor"), "load");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function closeEditTablesModal() {
|
|
|
|
|
|
document.getElementById("editTablesModal").classList.remove("show");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function editAppTables() {
|
|
|
|
|
|
showEditTablesModal();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function saveTablesFile() {
|
|
|
|
|
|
const editor = document.getElementById("tables-editor-content");
|
|
|
|
|
|
if (!editor) return;
|
|
|
|
|
|
|
|
|
|
|
|
fetch("/api/tables/source", {
|
|
|
|
|
|
method: "PUT",
|
|
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
|
|
body: JSON.stringify({ content: editor.value }),
|
|
|
|
|
|
})
|
|
|
|
|
|
.then((response) => {
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
|
closeEditTablesModal();
|
|
|
|
|
|
htmx.trigger(document.body, "taskCreated");
|
|
|
|
|
|
showToast("Tables saved and synced to database", "success");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showToast("Failed to save tables", "error");
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
|
console.error("Failed to save tables:", err);
|
|
|
|
|
|
showToast("Failed to save tables", "error");
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function showToast(message, type) {
|
|
|
|
|
|
// Simple toast notification
|
|
|
|
|
|
const toast = document.createElement("div");
|
|
|
|
|
|
toast.className = `toast toast-${type}`;
|
|
|
|
|
|
toast.textContent = message;
|
|
|
|
|
|
document.body.appendChild(toast);
|
|
|
|
|
|
setTimeout(() => toast.remove(), 3000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Transform form data before sending to API - using global listener
|
|
|
|
|
|
document.body.addEventListener("htmx:configRequest", function (event) {
|
|
|
|
|
|
const form = event.detail.elt;
|
|
|
|
|
|
if (form.id !== "newIntentForm") return;
|
|
|
|
|
|
|
|
|
|
|
|
const formData = event.detail.parameters;
|
|
|
|
|
|
|
|
|
|
|
|
// Convert due_date to ISO 8601 format if present
|
|
|
|
|
|
if (formData.due_date && formData.due_date.trim() !== "") {
|
|
|
|
|
|
const date = new Date(formData.due_date);
|
|
|
|
|
|
formData.due_date = date.toISOString();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
delete formData.due_date;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Remove empty optional fields
|
|
|
|
|
|
if (!formData.description || formData.description.trim() === "") {
|
|
|
|
|
|
delete formData.description;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Handle auto-detect intent type
|
|
|
|
|
|
if (formData.intent_type === "auto") {
|
|
|
|
|
|
delete formData.intent_type;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Currently selected task
|
|
|
|
|
|
window.selectedTaskId = null;
|
|
|
|
|
|
window.selectedTaskType = null;
|
|
|
|
|
|
|
|
|
|
|
|
// Select a task and load its details (global for HTMX onclick)
|
|
|
|
|
|
window.selectTask = function (taskId, taskType) {
|
|
|
|
|
|
window.selectedTaskId = taskId;
|
|
|
|
|
|
window.selectedTaskType = taskType || "task";
|
|
|
|
|
|
|
|
|
|
|
|
// Update UI selection state
|
|
|
|
|
|
document.querySelectorAll(".task-item, .task-card").forEach((el) => {
|
|
|
|
|
|
el.classList.remove("selected");
|
|
|
|
|
|
});
|
|
|
|
|
|
const selectedEl = document.querySelector(`[data-task-id="${taskId}"]`);
|
|
|
|
|
|
if (selectedEl) {
|
|
|
|
|
|
selectedEl.classList.add("selected");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Show detail panel content
|
|
|
|
|
|
document.getElementById("task-detail-empty").style.display = "none";
|
|
|
|
|
|
document.getElementById("task-detail-content").style.display = "block";
|
|
|
|
|
|
|
|
|
|
|
|
// Hide all type-specific sections
|
|
|
|
|
|
document.getElementById("goal-progress-section").style.display = "none";
|
|
|
|
|
|
document.getElementById("pending-fill-section").style.display = "none";
|
|
|
|
|
|
document.getElementById("scheduler-info-section").style.display = "none";
|
|
|
|
|
|
document.getElementById("monitor-info-section").style.display = "none";
|
|
|
|
|
|
document.getElementById("table-info-section").style.display = "none";
|
|
|
|
|
|
document.getElementById("app-info-section").style.display = "none";
|
|
|
|
|
|
|
|
|
|
|
|
// Load details based on type
|
|
|
|
|
|
const endpoint = getEndpointForType(taskType, taskId);
|
|
|
|
|
|
fetch(endpoint)
|
|
|
|
|
|
.then((response) => response.json())
|
|
|
|
|
|
.then((data) => {
|
|
|
|
|
|
populateDetails(data, taskType);
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
|
console.error("Failed to load details:", err);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function getEndpointForType(type, id) {
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
case "goal":
|
|
|
|
|
|
return `/api/goals/${id}`;
|
|
|
|
|
|
case "pending":
|
|
|
|
|
|
return `/api/pending-info/${id}`;
|
|
|
|
|
|
case "scheduler":
|
|
|
|
|
|
return `/api/schedulers/${id}`;
|
|
|
|
|
|
case "monitor":
|
|
|
|
|
|
return `/api/monitors/${id}`;
|
|
|
|
|
|
case "table":
|
|
|
|
|
|
return `/api/tables/${id}`;
|
|
|
|
|
|
case "app":
|
|
|
|
|
|
return `/api/apps/${id}`;
|
|
|
|
|
|
default:
|
|
|
|
|
|
return `/api/tasks/${id}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function populateDetails(data, type) {
|
|
|
|
|
|
document.getElementById("detail-title").textContent =
|
|
|
|
|
|
data.title || data.name || "Untitled";
|
|
|
|
|
|
document.getElementById("detail-status-text").textContent =
|
|
|
|
|
|
data.status || "unknown";
|
|
|
|
|
|
document.getElementById("detail-priority-text").textContent =
|
|
|
|
|
|
data.priority || "normal";
|
|
|
|
|
|
document.getElementById("detail-description").textContent =
|
|
|
|
|
|
data.description || "No description provided.";
|
|
|
|
|
|
|
|
|
|
|
|
const dueDateText = data.due_date
|
|
|
|
|
|
? new Date(data.due_date).toLocaleDateString()
|
|
|
|
|
|
: "No due date";
|
|
|
|
|
|
document.getElementById("detail-due-date-text").textContent = dueDateText;
|
|
|
|
|
|
|
|
|
|
|
|
const createdText = data.created_at
|
|
|
|
|
|
? new Date(data.created_at).toLocaleString()
|
|
|
|
|
|
: "-";
|
|
|
|
|
|
document.getElementById("detail-created-at").textContent = createdText;
|
|
|
|
|
|
|
|
|
|
|
|
// Show type-specific sections
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
case "goal":
|
|
|
|
|
|
document.getElementById("goal-progress-section").style.display = "block";
|
|
|
|
|
|
document.getElementById("goal-current-value").textContent = data.current_value || 0;
|
|
|
|
|
|
document.getElementById("goal-target-value").textContent = data.target_value || 0;
|
|
|
|
|
|
const percent = data.target_value ? Math.round((data.current_value / data.target_value) * 100) : 0;
|
|
|
|
|
|
document.getElementById("goal-percent").textContent = percent;
|
|
|
|
|
|
document.getElementById("goal-progress-fill").style.width = percent + "%";
|
|
|
|
|
|
document.getElementById("goal-last-action").textContent = "Last action: " + (data.last_action || "-");
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case "pending":
|
|
|
|
|
|
document.getElementById("pending-fill-section").style.display = "block";
|
|
|
|
|
|
document.getElementById("pending-fill-id").value = data.id;
|
|
|
|
|
|
document.getElementById("pending-fill-label").textContent = data.field_label || "Value";
|
|
|
|
|
|
document.getElementById("pending-reason").textContent = data.reason || "Required for app functionality";
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case "scheduler":
|
|
|
|
|
|
document.getElementById("scheduler-info-section").style.display = "block";
|
|
|
|
|
|
document.getElementById("scheduler-cron").textContent = "Cron: " + (data.cron || "-");
|
|
|
|
|
|
document.getElementById("scheduler-next").textContent = "Next run: " + (data.next_run || "-");
|
|
|
|
|
|
document.getElementById("scheduler-file").textContent = "File: .gbdialog/schedulers/" + (data.file || "-");
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case "monitor":
|
|
|
|
|
|
document.getElementById("monitor-info-section").style.display = "block";
|
|
|
|
|
|
document.getElementById("monitor-target").textContent = "Target: " + (data.target || "-");
|
|
|
|
|
|
document.getElementById("monitor-interval").textContent = "Interval: " + (data.interval || "-");
|
|
|
|
|
|
document.getElementById("monitor-last-check").textContent = "Last check: " + (data.last_check || "-");
|
|
|
|
|
|
document.getElementById("monitor-last-value").textContent = "Last value: " + (data.last_value || "-");
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case "table":
|
|
|
|
|
|
document.getElementById("table-info-section").style.display = "block";
|
|
|
|
|
|
document.getElementById("table-name").textContent = "Table: " + (data.name || "-");
|
|
|
|
|
|
document.getElementById("table-fields").textContent = "Fields: " + (data.fields?.join(", ") || "-");
|
|
|
|
|
|
document.getElementById("table-records").textContent = "Records: " + (data.record_count || 0);
|
|
|
|
|
|
document.getElementById("table-query-example").textContent = "FIND * IN " + (data.name || "table_name");
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case "app":
|
|
|
|
|
|
document.getElementById("app-info-section").style.display = "block";
|
|
|
|
|
|
document.getElementById("app-name").textContent = "App: " + (data.name || "-");
|
|
|
|
|
|
document.getElementById("app-url").textContent = "URL: /apps/" + (data.name || "-");
|
|
|
|
|
|
document.getElementById("app-tables").textContent = "Uses tables: " + (data.tables?.join(", ") || "-");
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Update task status (global for onclick)
|
|
|
|
|
|
window.updateTaskStatus = function (newStatus) {
|
|
|
|
|
|
if (!window.selectedTaskId) return;
|
|
|
|
|
|
|
|
|
|
|
|
fetch(`/api/tasks/${window.selectedTaskId}/status`, {
|
|
|
|
|
|
method: "PUT",
|
|
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
|
|
body: JSON.stringify({ status: newStatus }),
|
|
|
|
|
|
})
|
|
|
|
|
|
.then((response) => {
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
|
htmx.trigger(document.body, "taskCreated");
|
|
|
|
|
|
window.selectTask(window.selectedTaskId, window.selectedTaskType);
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((err) => console.error("Failed to update status:", err));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Delete selected task (global for onclick)
|
|
|
|
|
|
window.deleteSelectedTask = function () {
|
|
|
|
|
|
if (!window.selectedTaskId) return;
|
|
|
|
|
|
if (!confirm("Are you sure you want to delete this?")) return;
|
|
|
|
|
|
|
|
|
|
|
|
const endpoint = getEndpointForType(window.selectedTaskType, window.selectedTaskId);
|
|
|
|
|
|
fetch(endpoint, { method: "DELETE" })
|
|
|
|
|
|
.then((response) => {
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
|
window.selectedTaskId = null;
|
|
|
|
|
|
window.selectedTaskType = null;
|
|
|
|
|
|
document.getElementById("task-detail-empty").style.display = "flex";
|
|
|
|
|
|
document.getElementById("task-detail-content").style.display = "none";
|
|
|
|
|
|
htmx.trigger(document.body, "taskCreated");
|
|
|
|
|
|
showToast("Deleted successfully", "success");
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((err) => console.error("Failed to delete:", err));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Edit selected task (global for onclick)
|
|
|
|
|
|
window.editSelectedTask = function () {
|
|
|
|
|
|
if (!window.selectedTaskId) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (window.selectedTaskType === "table") {
|
|
|
|
|
|
showEditTablesModal();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
alert("Edit functionality for " + window.selectedTaskType + ": " + window.selectedTaskId);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Open app in new tab
|
|
|
|
|
|
window.openApp = function () {
|
|
|
|
|
|
if (window.selectedTaskType === "app" && window.selectedTaskId) {
|
|
|
|
|
|
window.open("/apps/" + window.selectedTaskId, "_blank");
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-12-15 23:16:09 -03:00
|
|
|
|
// Close modal on escape key
|
|
|
|
|
|
document.addEventListener("keydown", (e) => {
|
|
|
|
|
|
if (e.key === "Escape") {
|
|
|
|
|
|
closeNewIntentModal();
|
2025-12-27 22:38:37 -03:00
|
|
|
|
closeEditTablesModal();
|
2025-12-15 23:16:09 -03:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Close modal when clicking overlay
|
2025-12-27 22:38:37 -03:00
|
|
|
|
document.querySelectorAll(".modal-overlay").forEach((overlay) => {
|
|
|
|
|
|
overlay.addEventListener("click", (e) => {
|
2025-12-15 23:16:09 -03:00
|
|
|
|
if (e.target.classList.contains("modal-overlay")) {
|
|
|
|
|
|
closeNewIntentModal();
|
2025-12-27 22:38:37 -03:00
|
|
|
|
closeEditTablesModal();
|
2025-12-15 23:16:09 -03:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-12-27 22:38:37 -03:00
|
|
|
|
});
|
2025-12-15 23:16:09 -03:00
|
|
|
|
|
|
|
|
|
|
// Close modal after successful form submission
|
|
|
|
|
|
document.body.addEventListener("htmx:afterRequest", (e) => {
|
|
|
|
|
|
if (e.detail.elt.id === "newIntentForm" && e.detail.successful) {
|
|
|
|
|
|
closeNewIntentModal();
|
2025-12-27 22:38:37 -03:00
|
|
|
|
showToast("Intent created successfully", "success");
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Update stats counts on load
|
|
|
|
|
|
document.body.addEventListener("htmx:afterSwap", (e) => {
|
|
|
|
|
|
if (e.detail.target.id === "tasks-total-count") {
|
|
|
|
|
|
// Stats loaded, update pill counts
|
|
|
|
|
|
try {
|
|
|
|
|
|
const stats = JSON.parse(e.detail.target.textContent);
|
|
|
|
|
|
if (stats.total !== undefined) {
|
|
|
|
|
|
document.getElementById("count-all").textContent = stats.total;
|
|
|
|
|
|
document.getElementById("count-active").textContent = stats.running || 0;
|
|
|
|
|
|
document.getElementById("count-completed").textContent = stats.completed || 0;
|
|
|
|
|
|
document.getElementById("count-pending").textContent = stats.pending_count || 0;
|
|
|
|
|
|
document.getElementById("count-goals").textContent = stats.goals_count || 0;
|
|
|
|
|
|
document.getElementById("count-schedulers").textContent = stats.schedulers_count || 0;
|
|
|
|
|
|
document.getElementById("count-monitors").textContent = stats.monitors_count || 0;
|
|
|
|
|
|
document.getElementById("count-tables").textContent = stats.tables_count || 0;
|
|
|
|
|
|
document.getElementById("count-apps").textContent = stats.apps_count || 0;
|
|
|
|
|
|
e.detail.target.textContent = stats.total + " items";
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
// Not JSON, leave as is
|
|
|
|
|
|
}
|
2025-12-15 23:16:09 -03:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|