diff --git a/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderLayoutExample.tsx b/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderLayoutExample.tsx new file mode 100644 index 00000000..ed7e5175 --- /dev/null +++ b/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderLayoutExample.tsx @@ -0,0 +1,1240 @@ +import { + Activity, + CheckCircle2, + Clock, + Copy, + Eye, + GitBranch, + LayoutGrid, + List, + Pin, + Play, + Plus, + Trash2, +} from "lucide-react"; +import { useState } from "react"; +import { Button } from "@/features/ui/primitives/button"; +import { Checkbox } from "@/features/ui/primitives/checkbox"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/features/ui/primitives/dialog"; +import { Input } from "@/features/ui/primitives/input"; +import { StatPill } from "@/features/ui/primitives/pill"; +import { PillNavigation, type PillNavigationItem } from "@/features/ui/primitives/pill-navigation"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/features/ui/primitives/select"; +import { SelectableCard } from "@/features/ui/primitives/selectable-card"; +import { cn } from "@/features/ui/primitives/styles"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/features/ui/primitives/tooltip"; +import { AgentWorkOrderExample } from "./AgentWorkOrderExample"; + +const MOCK_REPOSITORIES = [ + { + id: "1", + name: "archon-frontend", + url: "https://github.com/coleam00/archon-ui", + pinned: true, + workOrderCounts: { pending: 1, create_branch: 1, plan: 0, execute: 0, commit: 1, create_pr: 0 }, + }, + { + id: "2", + name: "archon-backend", + url: "https://github.com/coleam00/archon-backend", + pinned: false, + workOrderCounts: { pending: 0, create_branch: 0, plan: 1, execute: 1, commit: 0, create_pr: 0 }, + }, + { + id: "3", + name: "archon-docs", + url: "https://github.com/coleam00/archon-docs", + pinned: false, + workOrderCounts: { pending: 0, create_branch: 0, plan: 0, execute: 0, commit: 0, create_pr: 1 }, + }, +]; + +type WorkOrderStatus = "pending" | "create_branch" | "plan" | "execute" | "commit" | "create_pr"; + +interface WorkOrder { + id: string; + repositoryId: string; + repositoryName: string; + request: string; + status: WorkOrderStatus; + steps: { + createBranch: boolean; + plan: boolean; + execute: boolean; + commit: boolean; + createPR: boolean; + }; + createdAt: string; +} + +const MOCK_WORK_ORDERS: WorkOrder[] = [ + { + id: "wo-1", + repositoryId: "1", + repositoryName: "archon-frontend", + request: "Add dark mode toggle to settings page", + status: "pending", + steps: { createBranch: true, plan: true, execute: true, commit: true, createPR: true }, + createdAt: "2024-01-15T10:30:00Z", + }, + { + id: "wo-2", + repositoryId: "1", + repositoryName: "archon-frontend", + request: "Refactor navigation component to use new design system", + status: "create_branch", + steps: { createBranch: true, plan: true, execute: true, commit: true, createPR: true }, + createdAt: "2024-01-15T09:15:00Z", + }, + { + id: "wo-3", + repositoryId: "2", + repositoryName: "archon-backend", + request: "Implement caching layer for API responses", + status: "plan", + steps: { createBranch: true, plan: true, execute: true, commit: true, createPR: true }, + createdAt: "2024-01-14T16:45:00Z", + }, + { + id: "wo-4", + repositoryId: "2", + repositoryName: "archon-backend", + request: "Add rate limiting to authentication endpoints", + status: "execute", + steps: { createBranch: true, plan: true, execute: true, commit: true, createPR: true }, + createdAt: "2024-01-14T14:20:00Z", + }, + { + id: "wo-5", + repositoryId: "1", + repositoryName: "archon-frontend", + request: "Fix responsive layout issues on mobile devices", + status: "commit", + steps: { createBranch: true, plan: true, execute: true, commit: true, createPR: true }, + createdAt: "2024-01-13T11:00:00Z", + }, + { + id: "wo-6", + repositoryId: "3", + repositoryName: "archon-docs", + request: "Update API documentation with new endpoints", + status: "create_pr", + steps: { createBranch: true, plan: true, execute: true, commit: true, createPR: true }, + createdAt: "2024-01-12T08:30:00Z", + }, +]; + +export const AgentWorkOrderLayoutExample = () => { + const [selectedRepositoryId, setSelectedRepositoryId] = useState("1"); + const [layoutMode, setLayoutMode] = useState<"horizontal" | "sidebar">("horizontal"); + const [sidebarExpanded, setSidebarExpanded] = useState(true); + const [showAddRepoModal, setShowAddRepoModal] = useState(false); + const [showNewWorkOrderModal, setShowNewWorkOrderModal] = useState(false); + const [workOrders, setWorkOrders] = useState(MOCK_WORK_ORDERS); + const [activeTab, setActiveTab] = useState("all"); + const [showDetailView, setShowDetailView] = useState(false); + const [selectedWorkOrderId, setSelectedWorkOrderId] = useState(null); + + const selectedRepository = MOCK_REPOSITORIES.find((r) => r.id === selectedRepositoryId); + const selectedWorkOrder = workOrders.find((wo) => wo.id === selectedWorkOrderId); + + // If showing detail view, render the detail component + if (showDetailView && selectedWorkOrder) { + return ( +
+ {/* Breadcrumb navigation */} +
+ + / + + / + {selectedWorkOrder.id} +
+ +
+ ); + } + + // Tab items for navigation + const tabItems: PillNavigationItem[] = [ + { id: "all", label: "All Work Orders", icon: }, + ]; + + // Add selected repository as a tab if one is selected (always show, even when viewing all) + if (selectedRepository) { + tabItems.push({ + id: selectedRepository.id, + label: selectedRepository.name, + icon: , + }); + } + + return ( +
+ {/* Layout Mode Toggle */} +
+
+ + +
+
+ + {layoutMode === "horizontal" ? ( + <> + {/* Horizontal Repository Cards - ONLY cards scroll, not whole page */} +
+
+
+ {MOCK_REPOSITORIES.map((repository) => ( + { + setSelectedRepositoryId(repository.id); + setActiveTab(repository.id); + }} + /> + ))} + {/* Add Repository Button */} + +
+
+
+ + {/* Orange Pill Navigation centered */} +
+ { + setActiveTab(id); + if (id !== "all") { + setSelectedRepositoryId(id); + } + }} + colorVariant="orange" + size="small" + showIcons={true} + showText={true} + hasSubmenus={false} + /> +
+ + {/* Work Orders Table */} + { + setWorkOrders((prev) => + prev.map((wo) => (wo.id === id ? { ...wo, status: "create_branch" as WorkOrderStatus } : wo)), + ); + }} + onViewDetails={(id) => { + setSelectedWorkOrderId(id); + setShowDetailView(true); + }} + showNewWorkOrderModal={showNewWorkOrderModal} + onNewWorkOrderModalChange={setShowNewWorkOrderModal} + /> + + ) : ( + /* Sidebar Mode */ +
+ {/* Left Sidebar - Collapsible Repository List */} + {sidebarExpanded && ( +
+
+

Repositories

+ +
+
+ {MOCK_REPOSITORIES.map((repository) => ( + { + setSelectedRepositoryId(repository.id); + setActiveTab(repository.id); + }} + /> + ))} +
+
+ )} + + {/* Main Content Area */} +
+ {/* Header with repository name, tabs, and actions inline */} +
+ {!sidebarExpanded && ( + + )} + + {/* Orange Pill Navigation - ALWAYS CENTERED */} +
+ { + setActiveTab(id); + if (id !== "all") { + setSelectedRepositoryId(id); + } + }} + colorVariant="orange" + size="small" + showIcons={true} + showText={true} + hasSubmenus={false} + /> +
+ + {/* Spacer for symmetry */} +
+
+ + {/* Work Orders Table - Full Width, NO extra spacing */} + { + setWorkOrders((prev) => + prev.map((wo) => (wo.id === id ? { ...wo, status: "create_branch" as WorkOrderStatus } : wo)), + ); + }} + onViewDetails={(id) => { + setSelectedWorkOrderId(id); + setShowDetailView(true); + }} + showNewWorkOrderModal={showNewWorkOrderModal} + onNewWorkOrderModalChange={setShowNewWorkOrderModal} + /> +
+
+ )} +
+ ); +}; + +// Repository Card using SelectableCard primitive +const RepositoryCard = ({ + repository, + isSelected, + onSelect, +}: { + repository: (typeof MOCK_REPOSITORIES)[0]; + isSelected: boolean; + onSelect: () => void; +}) => { + // Custom gradients for pinned vs selected vs default + const getBackgroundClass = () => { + if (repository.pinned) + return "bg-gradient-to-b from-purple-100/80 via-purple-50/30 to-purple-100/50 dark:from-purple-900/30 dark:via-purple-900/20 dark:to-purple-900/10"; + if (isSelected) + return "bg-gradient-to-b from-white/70 via-purple-50/20 to-white/50 dark:from-white/5 dark:via-purple-900/5 dark:to-black/20"; + return "bg-gradient-to-b from-white/80 to-white/60 dark:from-white/10 dark:to-black/30"; + }; + + // Calculate aggregated counts + const totalWorkOrders = + repository.workOrderCounts.pending + + repository.workOrderCounts.create_branch + + repository.workOrderCounts.plan + + repository.workOrderCounts.execute + + repository.workOrderCounts.commit + + repository.workOrderCounts.create_pr; + + const inProgressCount = + repository.workOrderCounts.create_branch + + repository.workOrderCounts.plan + + repository.workOrderCounts.execute + + repository.workOrderCounts.commit; + + const completedCount = repository.workOrderCounts.create_pr; + + return ( + + {/* Main content */} +
+ {/* Title */} +
+

+ {repository.name} +

+
+ + {/* Work order count pills - 3 aggregated statuses */} +
+ {/* Total pill */} +
+
+
+
+ + + Total + +
+
+ + {totalWorkOrders} + +
+
+
+ + {/* In Progress pill */} +
+
+
+
+ + + Active + +
+
+ + {inProgressCount} + +
+
+
+ + {/* Completed pill */} +
+
+
+
+ + + Done + +
+
+ + {completedCount} + +
+
+
+
+
+ + {/* Bottom bar with action icons */} +
+ {/* Pinned indicator with icon */} + {repository.pinned ? ( +
+
+ ) : ( +
+ )} + + {/* Action icons */} +
+ + + + + + Delete repository + + + + + + {repository.pinned ? "Unpin repository" : "Pin repository"} + + + + + + Duplicate repository + + +
+
+ + ); +}; + +// Sidebar Repository Card - mini card style with StatPills +const SidebarRepositoryCard = ({ + repository, + isSelected, + onSelect, +}: { + repository: (typeof MOCK_REPOSITORIES)[0]; + isSelected: boolean; + onSelect: () => void; +}) => { + const getBackgroundClass = () => { + if (repository.pinned) + return "bg-gradient-to-b from-purple-100/80 via-purple-50/30 to-purple-100/50 dark:from-purple-900/30 dark:via-purple-900/20 dark:to-purple-900/10"; + if (isSelected) + return "bg-gradient-to-b from-white/70 via-purple-50/20 to-white/50 dark:from-white/5 dark:via-purple-900/5 dark:to-black/20"; + return "bg-gradient-to-b from-white/80 to-white/60 dark:from-white/10 dark:to-black/30"; + }; + + // Calculate aggregated counts + const totalWorkOrders = + repository.workOrderCounts.pending + + repository.workOrderCounts.create_branch + + repository.workOrderCounts.plan + + repository.workOrderCounts.execute + + repository.workOrderCounts.commit + + repository.workOrderCounts.create_pr; + + const inProgressCount = + repository.workOrderCounts.create_branch + + repository.workOrderCounts.plan + + repository.workOrderCounts.execute + + repository.workOrderCounts.commit; + + const completedCount = repository.workOrderCounts.create_pr; + + return ( + +
+ {/* Title */} +
+

+ {repository.name} +

+ {repository.pinned && ( +
+
+ )} +
+ + {/* Status Pills - all 3 on one row */} +
+ } /> + } /> + } /> +
+
+
+ ); +}; + +// Work Orders Table View +const WorkOrdersTableView = ({ + workOrders, + selectedRepositoryId, + onStartWorkOrder, + onViewDetails, + showNewWorkOrderModal, + onNewWorkOrderModalChange, +}: { + workOrders: WorkOrder[]; + selectedRepositoryId?: string; + onStartWorkOrder: (id: string) => void; + onViewDetails: (id: string) => void; + showNewWorkOrderModal: boolean; + onNewWorkOrderModalChange: (open: boolean) => void; +}) => { + // Filter work orders based on selected repository + const filteredWorkOrders = selectedRepositoryId + ? workOrders.filter((wo) => wo.repositoryId === selectedRepositoryId) + : workOrders; + + return ( +
+ {/* Header with New Work Order button */} +
+

Work Orders

+ +
+ +
+ + + + + + + + + + + {filteredWorkOrders.map((workOrder, index) => ( + onStartWorkOrder(workOrder.id)} + onViewDetails={() => onViewDetails(workOrder.id)} + /> + ))} + +
+ + Work Order ID + + Request Summary + StatusActions
+
+
+ ); +}; + +// Work Order Row with status-based styling +const WorkOrderRow = ({ + workOrder, + index, + onStart, + onViewDetails, +}: { + workOrder: WorkOrder; + index: number; + onStart: () => void; + onViewDetails: () => void; +}) => { + // Status colors - STATIC lookup with all properties + const statusColors: Record< + WorkOrderStatus, + { color: "pink" | "cyan" | "blue" | "orange" | "purple" | "green"; edge: string; glow: string; label: string } + > = { + pending: { + color: "pink", + edge: "bg-pink-500", + glow: "rgba(236,72,153,0.5)", + label: "Pending", + }, + create_branch: { + color: "cyan", + edge: "bg-cyan-500", + glow: "rgba(34,211,238,0.5)", + label: "+ Branch", + }, + plan: { + color: "blue", + edge: "bg-blue-500", + glow: "rgba(59,130,246,0.5)", + label: "Planning", + }, + execute: { + color: "orange", + edge: "bg-orange-500", + glow: "rgba(249,115,22,0.5)", + label: "Executing", + }, + commit: { + color: "purple", + edge: "bg-purple-500", + glow: "rgba(168,85,247,0.5)", + label: "Commit", + }, + create_pr: { + color: "green", + edge: "bg-green-500", + glow: "rgba(34,197,94,0.5)", + label: "Create PR", + }, + }; + + const colors = statusColors[workOrder.status]; + + return ( + + {/* Status indicator - glowing circle */} + +
+
+
+ + + {/* Work Order ID */} + + {workOrder.id} + + + {/* Request Summary */} + +

{workOrder.request}

+ + + {/* Status Badge - using StatPill */} + + + + + {/* Actions */} + + {workOrder.status === "pending" ? ( + + ) : ( + + )} + + + ); +}; + +// Add Repository Modal +const AddRepositoryModal = ({ open, onOpenChange }: { open: boolean; onOpenChange: (open: boolean) => void }) => { + const [repositoryName, setRepositoryName] = useState(""); + const [repositoryUrl, setRepositoryUrl] = useState(""); + const [error, setError] = useState(""); + + const handleSubmit = () => { + // Validation + if (!repositoryName.trim()) { + setError("Repository name is required"); + return; + } + if (!repositoryUrl.trim()) { + setError("Repository URL is required"); + return; + } + if (!repositoryUrl.startsWith("https://")) { + setError("Repository URL must start with https://"); + return; + } + + // Success - add to repositories (mock) + console.log("Adding repository:", { repositoryName, repositoryUrl }); + setRepositoryName(""); + setRepositoryUrl(""); + setError(""); + onOpenChange(false); + }; + + return ( + + + + + + + Add Repository + +
+ {/* Repository Name */} +
+ + { + setRepositoryName(e.target.value); + setError(""); + }} + aria-label="Repository name" + /> +
+ + {/* Repository URL */} +
+ + { + setRepositoryUrl(e.target.value); + setError(""); + }} + aria-label="Repository URL" + /> +
+ + {/* Error Message */} + {error &&

{error}

} + + {/* Actions */} +
+ + +
+
+
+
+ ); +}; + +// New Work Order Modal +const NewWorkOrderModal = ({ open, onOpenChange }: { open: boolean; onOpenChange: (open: boolean) => void }) => { + const [selectedRepoId, setSelectedRepoId] = useState(""); + const [requestText, setRequestText] = useState(""); + const [stepsState, setStepsState] = useState({ + createBranch: true, + plan: true, + execute: true, + commit: false, + createPR: false, + }); + const [error, setError] = useState(""); + + // Dependency logic + const canEnableCommit = stepsState.execute; + const canEnableCreatePR = stepsState.execute; + + const handleSubmit = () => { + // Validation + if (!selectedRepoId) { + setError("Please select a repository"); + return; + } + if (!requestText.trim()) { + setError("Request is required"); + return; + } + if ( + !stepsState.createBranch && + !stepsState.plan && + !stepsState.execute && + !stepsState.commit && + !stepsState.createPR + ) { + setError("At least one step must be selected"); + return; + } + + // Success - create work order (mock) + console.log("Creating work order:", { selectedRepoId, requestText, steps: stepsState }); + setSelectedRepoId(""); + setRequestText(""); + setStepsState({ + createBranch: true, + plan: true, + execute: true, + commit: false, + createPR: false, + }); + setError(""); + onOpenChange(false); + }; + + return ( + + + + + + + Create Work Order + +
+ {/* Repository Select */} +
+ + +
+ + {/* Request Input */} +
+ + { + setRequestText(e.target.value); + setError(""); + }} + aria-label="Work order request" + /> +
+ + {/* Step Toggles */} +
+ +
+
+ { + setStepsState({ ...stepsState, createBranch: checked === true }); + setError(""); + }} + aria-label="Create branch step" + /> + +
+
+ { + setStepsState({ ...stepsState, plan: checked === true }); + setError(""); + }} + aria-label="Plan step" + /> + +
+
+ { + const newExecute = checked === true; + setStepsState({ + ...stepsState, + execute: newExecute, + // Auto-disable dependent steps if execute is disabled + commit: newExecute ? stepsState.commit : false, + createPR: newExecute ? stepsState.createPR : false, + }); + setError(""); + }} + aria-label="Execute step" + /> + +
+
+ { + setStepsState({ ...stepsState, commit: checked === true }); + setError(""); + }} + disabled={!canEnableCommit} + className={cn(!canEnableCommit && "opacity-50 cursor-not-allowed")} + aria-label="Commit step" + aria-disabled={!canEnableCommit} + /> + +
+
+ { + setStepsState({ ...stepsState, createPR: checked === true }); + setError(""); + }} + disabled={!canEnableCreatePR} + className={cn(!canEnableCreatePR && "opacity-50 cursor-not-allowed")} + aria-label="Create PR step" + aria-disabled={!canEnableCreatePR} + /> + +
+
+
+ + {/* Error Message */} + {error &&

{error}

} + + {/* Actions */} +
+ + +
+
+
+
+ ); +}; diff --git a/archon-ui-main/src/features/style-guide/tabs/LayoutsTab.tsx b/archon-ui-main/src/features/style-guide/tabs/LayoutsTab.tsx index 6f19cce4..b07deb84 100644 --- a/archon-ui-main/src/features/style-guide/tabs/LayoutsTab.tsx +++ b/archon-ui-main/src/features/style-guide/tabs/LayoutsTab.tsx @@ -1,6 +1,7 @@ import { Briefcase, Database, FileText, FolderKanban, Navigation, Settings } from "lucide-react"; import { useState } from "react"; import { AgentWorkOrderExample } from "../layouts/AgentWorkOrderExample"; +import { AgentWorkOrderLayoutExample } from "../layouts/AgentWorkOrderLayoutExample"; import { DocumentBrowserExample } from "../layouts/DocumentBrowserExample"; import { KnowledgeLayoutExample } from "../layouts/KnowledgeLayoutExample"; import { NavigationExplanation } from "../layouts/NavigationExplanation"; @@ -75,9 +76,9 @@ export const LayoutsTab = () => {

Agent Work Orders Layout

- Workflow progress visualization with step-by-step history and integrated document editing. + Repository-based work order management with table view, status tracking, and integrated detail view.

- +
); default: diff --git a/archon-ui-main/src/features/ui/primitives/button.tsx b/archon-ui-main/src/features/ui/primitives/button.tsx index 15374658..b7e7914b 100644 --- a/archon-ui-main/src/features/ui/primitives/button.tsx +++ b/archon-ui-main/src/features/ui/primitives/button.tsx @@ -2,7 +2,7 @@ import React from "react"; import { cn } from "./styles"; export interface ButtonProps extends React.ButtonHTMLAttributes { - variant?: "default" | "destructive" | "outline" | "ghost" | "link" | "cyan" | "knowledge"; // Tron-style purple button used on Knowledge Base + variant?: "default" | "destructive" | "outline" | "ghost" | "link" | "cyan" | "knowledge" | "green" | "blue"; // Tron-style glass buttons size?: "default" | "sm" | "lg" | "icon" | "xs"; loading?: boolean; children: React.ReactNode; @@ -88,6 +88,30 @@ export const Button = React.forwardRef( "dark:hover:shadow-[0_0_25px_rgba(168,85,247,0.7)]", "focus-visible:ring-purple-500", ), + green: cn( + "backdrop-blur-md", + "bg-gradient-to-b from-green-100/80 to-white/60", + "dark:from-green-500/20 dark:to-green-500/10", + "text-green-700 dark:text-green-100", + "border border-green-300/50 dark:border-green-500/50", + "hover:from-green-200/90 hover:to-green-100/70", + "dark:hover:from-green-400/30 dark:hover:to-green-500/20", + "hover:shadow-[0_0_20px_rgba(34,197,94,0.5)]", + "dark:hover:shadow-[0_0_25px_rgba(34,197,94,0.7)]", + "focus-visible:ring-green-500", + ), + blue: cn( + "backdrop-blur-md", + "bg-gradient-to-b from-blue-100/80 to-white/60", + "dark:from-blue-500/20 dark:to-blue-500/10", + "text-blue-700 dark:text-blue-100", + "border border-blue-300/50 dark:border-blue-500/50", + "hover:from-blue-200/90 hover:to-blue-100/70", + "dark:hover:from-blue-400/30 dark:hover:to-blue-500/20", + "hover:shadow-[0_0_20px_rgba(59,130,246,0.5)]", + "dark:hover:shadow-[0_0_25px_rgba(59,130,246,0.7)]", + "focus-visible:ring-blue-500", + ), }; type ButtonSize = NonNullable;