feat(FN-848): merge Activity and Agent Log into unified Logs tab
- Consolidate separate Activity and Agent Log tabs into a single Logs tab with toggle - Add toggle UI to switch between activity log and agent log views - Add CSS styles for the new toggle component and log tab layout - Simplify TaskDetailModal tab structure by removing two tabs in favor of one
This commit is contained in:
@@ -149,7 +149,8 @@ export function TaskDetailModal({
|
||||
addToast,
|
||||
githubTokenConfigured,
|
||||
}: TaskDetailModalProps) {
|
||||
const [activeTab, setActiveTab] = useState<"definition" | "activity" | "agent-log" | "changes" | "commits" | "comments" | "model">("definition");
|
||||
const [activeTab, setActiveTab] = useState<"definition" | "logs" | "changes" | "commits" | "comments" | "model">("definition");
|
||||
const [logSubview, setLogSubview] = useState<"activity" | "agent-log">("activity");
|
||||
const [attachments, setAttachments] = useState<TaskAttachment[]>(task.attachments || []);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [dependencies, setDependencies] = useState<string[]>(task.dependencies || []);
|
||||
@@ -314,7 +315,7 @@ export function TaskDetailModal({
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const { entries: agentLogEntries, loading: agentLogLoading } = useAgentLogs(
|
||||
task.id,
|
||||
activeTab === "agent-log",
|
||||
activeTab === "logs" && logSubview === "agent-log",
|
||||
projectId,
|
||||
);
|
||||
useEffect(() => {
|
||||
@@ -677,7 +678,7 @@ export function TaskDetailModal({
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`detail-body${activeTab === "agent-log" && !isEditing ? " detail-body--agent-log" : ""}`}>
|
||||
<div className={`detail-body${activeTab === "logs" && logSubview === "agent-log" && !isEditing ? " detail-body--agent-log" : ""}`}>
|
||||
{isEditing ? (
|
||||
<div className="modal-edit-form">
|
||||
<TaskForm
|
||||
@@ -735,16 +736,10 @@ export function TaskDetailModal({
|
||||
Definition
|
||||
</button>
|
||||
<button
|
||||
className={`detail-tab${activeTab === "activity" ? " detail-tab-active" : ""}`}
|
||||
onClick={() => setActiveTab("activity")}
|
||||
className={`detail-tab${activeTab === "logs" ? " detail-tab-active" : ""}`}
|
||||
onClick={() => setActiveTab("logs")}
|
||||
>
|
||||
Activity
|
||||
</button>
|
||||
<button
|
||||
className={`detail-tab${activeTab === "agent-log" ? " detail-tab-active" : ""}`}
|
||||
onClick={() => setActiveTab("agent-log")}
|
||||
>
|
||||
Agent Log
|
||||
Logs
|
||||
</button>
|
||||
{(task.column === "in-progress" || task.column === "in-review" || task.column === "done") && (
|
||||
<button
|
||||
@@ -779,14 +774,53 @@ export function TaskDetailModal({
|
||||
<div className="detail-section">
|
||||
<ModelSelectorTab task={task} addToast={addToast} />
|
||||
</div>
|
||||
) : activeTab === "agent-log" ? (
|
||||
<div className="detail-section detail-section--agent-log">
|
||||
<AgentLogViewer
|
||||
entries={agentLogEntries}
|
||||
loading={agentLogLoading}
|
||||
executorModel={resolveEffectiveExecutor(task, settings)}
|
||||
validatorModel={resolveEffectiveValidator(task, settings)}
|
||||
/>
|
||||
) : activeTab === "logs" ? (
|
||||
<div className={`detail-section${logSubview === "agent-log" ? " detail-section--agent-log" : ""}`}>
|
||||
<div className="log-subview-toggle">
|
||||
<button
|
||||
className={`log-subview-btn${logSubview === "activity" ? " log-subview-btn-active" : ""}`}
|
||||
onClick={() => setLogSubview("activity")}
|
||||
>
|
||||
Activity
|
||||
</button>
|
||||
<button
|
||||
className={`log-subview-btn${logSubview === "agent-log" ? " log-subview-btn-active" : ""}`}
|
||||
onClick={() => setLogSubview("agent-log")}
|
||||
>
|
||||
Agent Log
|
||||
</button>
|
||||
</div>
|
||||
{logSubview === "agent-log" ? (
|
||||
<AgentLogViewer
|
||||
entries={agentLogEntries}
|
||||
loading={agentLogLoading}
|
||||
executorModel={resolveEffectiveExecutor(task, settings)}
|
||||
validatorModel={resolveEffectiveValidator(task, settings)}
|
||||
/>
|
||||
) : (
|
||||
<div className="detail-activity">
|
||||
<h4>Activity</h4>
|
||||
{task.log && task.log.length > 0 ? (
|
||||
<div className="detail-activity-list">
|
||||
{[...task.log].reverse().map((entry, i) => (
|
||||
<div key={i} className="detail-log-entry">
|
||||
<div className="detail-log-header">
|
||||
<span className="detail-log-timestamp">
|
||||
{formatTimestamp(entry.timestamp)}
|
||||
</span>
|
||||
<span className="detail-log-action">{entry.action}</span>
|
||||
</div>
|
||||
{entry.outcome && (
|
||||
<div className="detail-log-outcome">{entry.outcome}</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="detail-log-empty">(no activity)</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : activeTab === "changes" ? (
|
||||
<TaskChangesTab taskId={task.id} worktree={task.worktree} projectId={projectId} column={task.column} mergeDetails={task.mergeDetails} />
|
||||
@@ -794,29 +828,6 @@ export function TaskDetailModal({
|
||||
<CommitDiffTab commitSha={task.mergeDetails?.commitSha ?? ""} mergeDetails={task.mergeDetails} />
|
||||
) : activeTab === "comments" ? (
|
||||
<TaskComments task={task} addToast={addToast} projectId={projectId} />
|
||||
) : activeTab === "activity" ? (
|
||||
<div className="detail-section detail-activity">
|
||||
<h4>Activity</h4>
|
||||
{task.log && task.log.length > 0 ? (
|
||||
<div className="detail-activity-list">
|
||||
{[...task.log].reverse().map((entry, i) => (
|
||||
<div key={i} className="detail-log-entry">
|
||||
<div className="detail-log-header">
|
||||
<span className="detail-log-timestamp">
|
||||
{formatTimestamp(entry.timestamp)}
|
||||
</span>
|
||||
<span className="detail-log-action">{entry.action}</span>
|
||||
</div>
|
||||
{entry.outcome && (
|
||||
<div className="detail-log-outcome">{entry.outcome}</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="detail-log-empty">(no activity)</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Summary section - only for done tasks with summary */}
|
||||
|
||||
@@ -4956,6 +4956,41 @@ body {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* === Log Subview Toggle === */
|
||||
.log-subview-toggle {
|
||||
display: inline-flex;
|
||||
gap: 0;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 2px;
|
||||
margin-bottom: var(--space-md);
|
||||
}
|
||||
|
||||
.log-subview-btn {
|
||||
padding: 4px 12px;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
transition: color var(--transition-fast), background var(--transition-fast);
|
||||
}
|
||||
|
||||
.log-subview-btn:hover {
|
||||
color: var(--text);
|
||||
background: var(--card-hover);
|
||||
}
|
||||
|
||||
.log-subview-btn-active {
|
||||
color: var(--text);
|
||||
background: var(--surface);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
/* === Mobile Responsive Overrides ===
|
||||
On narrow viewports (≤768px) the board switches from a 5-column grid to a
|
||||
horizontally-scrollable flex layout so only one scrollbar appears. Each
|
||||
|
||||
Reference in New Issue
Block a user