diff --git a/packages/frontend/src/pages/CustomCommands.test.tsx b/packages/frontend/src/pages/CustomCommands.test.tsx index 9e1165eaf..ac310cf5d 100644 --- a/packages/frontend/src/pages/CustomCommands.test.tsx +++ b/packages/frontend/src/pages/CustomCommands.test.tsx @@ -450,7 +450,7 @@ describe('CustomCommandsPage', () => { const coinflipCard = screen .getByText('/coinflip') - .closest('[class*="p-4"]') + .closest('[class*="surface-panel"]') expect(coinflipCard).toHaveClass('opacity-60') }) diff --git a/packages/frontend/src/pages/CustomCommands.tsx b/packages/frontend/src/pages/CustomCommands.tsx index 1e6280575..f4b99412f 100644 --- a/packages/frontend/src/pages/CustomCommands.tsx +++ b/packages/frontend/src/pages/CustomCommands.tsx @@ -1,7 +1,5 @@ import { useState, useEffect, useMemo } from 'react' -import { motion, AnimatePresence } from 'framer-motion' -import { Terminal, Search, X, Code } from 'lucide-react' -import Card from '@/components/ui/Card' +import { Search, X, Code, ChevronDown } from 'lucide-react' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Switch } from '@/components/ui/switch' @@ -30,9 +28,8 @@ export default function CustomCommandsPage() { const [commands, setCommands] = useState([]) const [loading, setLoading] = useState(true) const [searchQuery, setSearchQuery] = useState('') - const [selectedCategory, setSelectedCategory] = useState( - null, - ) + const [selectedCategory, setSelectedCategory] = useState(null) + const [expandedCommand, setExpandedCommand] = useState(null) useEffect(() => { if (!selectedGuild?.id) return @@ -53,9 +50,7 @@ export default function CustomCommandsPage() { const matchesSearch = !searchQuery || cmd.name.toLowerCase().includes(searchQuery.toLowerCase()) || - cmd.description - .toLowerCase() - .includes(searchQuery.toLowerCase()) + cmd.description.toLowerCase().includes(searchQuery.toLowerCase()) const matchesCat = !selectedCategory || cmd.category === selectedCategory return matchesSearch && matchesCat @@ -79,7 +74,7 @@ export default function CustomCommandsPage() { if (!selectedGuild) { return (
- +

No Server Selected

@@ -92,6 +87,7 @@ export default function CustomCommandsPage() { return (
+ {/* Header */}

Custom Commands @@ -101,38 +97,36 @@ export default function CustomCommandsPage() {

- {/* Filters */} - -
-
- - setSearchQuery(e.target.value)} - className='pl-9 bg-lucky-bg-tertiary border-lucky-border text-white placeholder:text-lucky-text-tertiary' - /> - {searchQuery && ( - - )} -
+ {/* Search + Filters */} +
+
+ + setSearchQuery(e.target.value)} + className='pl-9 bg-lucky-bg-tertiary border-lucky-border text-lucky-text-primary placeholder:text-lucky-text-tertiary' + /> + {searchQuery && ( + + )}
{/* Category chips */} {categories.length > 0 && ( -
+
))}
)} - +
- {/* Commands Grid */} -
+ {/* Commands List */} +
{loading ? ( - Array.from({ length: 9 }).map((_, i) => ( - -
- -
- - -
- + Array.from({ length: 6 }).map((_, i) => ( +
+ +
+ +
- - + +
)) ) : filtered.length > 0 ? ( - - {filtered.map((cmd, i) => ( - ( +
+ {/* Row header */} + + + {/* Expanded details */} + {expandedCommand === cmd.id && ( +
+
+

+ Description +

+

{cmd.description}

+
+
+ )} +
+ )) ) : ( -
- -

+

+ +

No commands found

-

+

{searchQuery || selectedCategory ? 'Try adjusting your filters' : 'Commands will appear here'} diff --git a/packages/frontend/src/pages/GuildAutomation.test.tsx b/packages/frontend/src/pages/GuildAutomation.test.tsx index 2dd55f500..7f665100d 100644 --- a/packages/frontend/src/pages/GuildAutomation.test.tsx +++ b/packages/frontend/src/pages/GuildAutomation.test.tsx @@ -135,7 +135,7 @@ describe('GuildAutomation', () => { await waitFor(() => { expect(screen.getByText('Guild Automation')).toBeInTheDocument() - expect(screen.getByText('Automation Status')).toBeInTheDocument() + expect(screen.getByText(/Automation Status/)).toBeInTheDocument() }) }) @@ -362,7 +362,7 @@ describe('GuildAutomation', () => { await waitFor(() => screen.getByText('Manifest')) - const expandButton = screen.getByText('Expand') + const expandButton = screen.getByRole('button', { name: /Expand/i }) await user.click(expandButton) await waitFor(() => { @@ -392,7 +392,7 @@ describe('GuildAutomation', () => { await waitFor(() => screen.getByText('Manifest')) - const expandButton = screen.getByText('Expand') + const expandButton = screen.getByRole('button', { name: /Expand/i }) await user.click(expandButton) await waitFor(() => screen.getByText('Save Manifest')) @@ -425,7 +425,7 @@ describe('GuildAutomation', () => { await waitFor(() => screen.getByText('Manifest')) - const expandButton = screen.getByText('Expand') + const expandButton = screen.getByRole('button', { name: /Expand/i }) await user.click(expandButton) await waitFor(() => screen.getByText('Save Manifest')) @@ -515,6 +515,7 @@ describe('GuildAutomation', () => { }) test('displays error message in run card when error exists', async () => { + const user = userEvent.setup() const runWithError: AutomationRun = { ...mockRun, status: 'failed', @@ -534,6 +535,15 @@ describe('GuildAutomation', () => { , ) + // Wait for the run card to render + await waitFor(() => { + expect(screen.getByText('plan')).toBeInTheDocument() + }) + + // Expand the run card to show the error message + const runCard = screen.getByText('plan').closest('button') + await user.click(runCard!) + await waitFor(() => { expect( screen.getByText('Failed to create role: permission denied'), @@ -556,7 +566,7 @@ describe('GuildAutomation', () => { , ) - await waitFor(() => screen.getByText('Automation Status')) + await waitFor(() => screen.getByText(/Automation Status/)) vi.clearAllMocks() @@ -612,7 +622,7 @@ describe('GuildAutomation', () => { await waitFor(() => screen.getByText('Manifest')) - const expandButton = screen.getByText('Expand') + const expandButton = screen.getByRole('button', { name: /Expand/i }) await user.click(expandButton) await waitFor(() => { diff --git a/packages/frontend/src/pages/GuildAutomation.tsx b/packages/frontend/src/pages/GuildAutomation.tsx index e7c5a66d7..88256c997 100644 --- a/packages/frontend/src/pages/GuildAutomation.tsx +++ b/packages/frontend/src/pages/GuildAutomation.tsx @@ -1,5 +1,4 @@ import { useState, useCallback, useEffect } from 'react' -import { motion, AnimatePresence } from 'framer-motion' import { GitBranch, Play, @@ -16,7 +15,6 @@ import { } from 'lucide-react' import { useGuildStore } from '@/stores/guildStore' import { api } from '@/services/api' -import Card from '@/components/ui/Card' import Button from '@/components/ui/Button' import EmptyState from '@/components/ui/EmptyState' import SectionHeader from '@/components/ui/SectionHeader' @@ -66,46 +64,45 @@ function RunCard({ run }: { run: AutomationRun }) { const [expanded, setExpanded] = useState(false) const date = new Date(run.createdAt).toLocaleString() return ( - -

-
+
+ + expanded ? : )}
-
- - {expanded && run.summary && ( - -

- {run.summary} -

-
- )} -
- {run.error && ( -

{run.error}

+ + {expanded && ( + <> +
+
+ {run.summary && ( +

+ {run.summary} +

+ )} + {run.error && ( +

+ {run.error} +

+ )} +
+ )} - +
) } @@ -118,9 +115,17 @@ function PlanResultView({ result }: { result: PlanResult }) { {result.changes.map((change, i) => (
- + {change.action} {change.type} @@ -140,9 +145,7 @@ function ApplyResultView({ result }: { result: ApplyResult }) {
{result.applied} applied - {result.failed > 0 && ( - {result.failed} failed - )} + {result.failed > 0 && {result.failed} failed}

{result.summary}

{result.changes.length > 0 && ( @@ -150,10 +153,12 @@ function ApplyResultView({ result }: { result: ApplyResult }) { {result.changes.map((change, i) => (
- {change.status} + + {change.status} + {change.type} {change.resource} {change.error && ( @@ -295,9 +300,9 @@ export default function GuildAutomation() { description='Manage your guild configuration as code — plan, apply, and track changes.' /> - {/* Status + Actions */} - -
+ {/* Status + Action Bar (Polaris structured actions) */} +
+

Automation Status

{loading ? ( @@ -305,17 +310,21 @@ export default function GuildAutomation() { ) : status ? ( {status} ) : ( - unknown + + unknown + )}
+
- +
- {/* Plan / Apply Results */} - - {planResult && ( - - -
- -

Plan Result

-
- -
-
- )} - {applyResult && ( - - -
- -

Apply Result

-
- -
-
- )} -
+ {/* Plan / Apply Results (surface-panel groups) */} + {planResult && ( +
+
+ +

Plan Result

+
+
+ +
+ )} + {applyResult && ( +
+
+ +

Apply Result

+
+
+ +
+ )} - {/* Manifest Editor */} - -
+ {/* Manifest Editor (collapsible surface-panel) */} +
+ -
- {loading ? ( - - ) : ( - - {manifestExpanded && ( - - {!manifest && ( -

- - No manifest found. Paste or write a manifest below to get started. -

- )} -