generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add infrastructure tabs and state
- Loading branch information
1 parent
cc2294f
commit 772b013
Showing
11 changed files
with
213 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { expect, ftlTest } from './ftl-test' | ||
|
||
ftlTest('shows infrastructure', async ({ page }) => { | ||
const infrastructureNavItem = page.getByRole('link', { name: 'Infrastructure' }) | ||
await infrastructureNavItem.click() | ||
await expect(page).toHaveURL(/\/infrastructure$/) | ||
|
||
const controllersTab = await page.getByRole('button', { name: 'Controllers' }); | ||
await expect(controllersTab).toBeVisible(); | ||
|
||
const runnersTab = await page.getByRole('button', { name: 'Runners' }); | ||
await expect(runnersTab).toBeVisible(); | ||
|
||
const deploymentsTab = await page.getByRole('button', { name: 'Deployments' }); | ||
await expect(deploymentsTab).toBeVisible(); | ||
|
||
const routesTab = await page.getByRole('button', { name: 'Routes' }); | ||
await expect(routesTab).toBeVisible(); | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { classNames } from '../utils' | ||
|
||
export const Pill = ({ text, className }: { text: string; className?: string }) => { | ||
return ( | ||
<span | ||
className={classNames( | ||
'bg-gray-100 text-gray-500 dark:text-gray-400 dark:bg-gray-700 rounded-full px-2.5 py-0.5 text-xs font-medium inline-block', | ||
className, | ||
)} | ||
> | ||
{text} | ||
</span> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { type FC, useEffect, useState } from 'react' | ||
import { classNames } from '../utils' | ||
import { Pill } from './Pill' | ||
|
||
interface Tab { | ||
name: string | ||
id: string | ||
count?: number | ||
} | ||
|
||
interface TabsProps { | ||
tabs: Tab[] | ||
initialTabId?: string | ||
onTabClick?: (tabId: string) => void | ||
} | ||
|
||
export const Tabs: FC<TabsProps> = ({ tabs, initialTabId, onTabClick }) => { | ||
const [selectedTabId, setSelectedTabId] = useState<string | undefined>(initialTabId || tabs[0]?.id) | ||
|
||
useEffect(() => { | ||
if (initialTabId) { | ||
setSelectedTabId(initialTabId) | ||
} | ||
}, [initialTabId]) | ||
|
||
const handleTabClick = (tabId: string) => { | ||
setSelectedTabId(tabId) | ||
if (onTabClick) { | ||
onTabClick(tabId) | ||
} | ||
} | ||
|
||
return ( | ||
<div className='border-b border-gray-200 dark:border-white/10'> | ||
<nav aria-label='Tabs' className='-mb-px flex space-x-8'> | ||
{tabs.map((tab) => ( | ||
<button | ||
key={tab.id} | ||
type='button' | ||
aria-current={selectedTabId === tab.id ? 'page' : undefined} | ||
onClick={() => handleTabClick(tab.id)} | ||
className={classNames( | ||
selectedTabId === tab.id | ||
? 'border-indigo-500 text-indigo-500' | ||
: 'border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-indigo-400 hover:border-gray-200 dark:hover:border-indigo-400 ', | ||
'flex whitespace-nowrap border-b-2 px-1 py-4 text-sm font-medium', | ||
)} | ||
> | ||
{tab.name} | ||
{tab.count && ( | ||
<Pill | ||
text={`${tab.count}`} | ||
className={classNames(selectedTabId === tab.id ? 'bg-indigo-100 text-indigo-600 dark:bg-indigo-500 dark:text-indigo-100' : '', 'ml-2')} | ||
/> | ||
)} | ||
</button> | ||
))} | ||
</nav> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const ControllersList = () => { | ||
return <>Controllers Content</> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const DeploymentsList = () => { | ||
return <>Deployments Content</> | ||
} |
69 changes: 68 additions & 1 deletion
69
frontend/src/features/infrastructure/InfrastructurePage.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,70 @@ | ||
import { useEffect, useState } from 'react' | ||
import { useSearchParams } from 'react-router-dom' | ||
import { useStatus } from '../../api/status/use-status' | ||
import { Tabs } from '../../components/Tabs' | ||
import { ControllersList } from './ControllersList' | ||
import { DeploymentsList } from './DeploymentsList' | ||
import { RoutesList } from './RoutesList' | ||
import { RunnersList } from './RunnersList' | ||
|
||
export const InfrastructurePage = () => { | ||
return <>Infrastructure</> | ||
const status = useStatus() | ||
const [searchParams, setSearchParams] = useSearchParams() | ||
|
||
const [tabs, setTabs] = useState([ | ||
{ name: 'Controllers', id: 'controllers' }, | ||
{ name: 'Runners', id: 'runners' }, | ||
{ name: 'Deployments', id: 'deployments' }, | ||
{ name: 'Routes', id: 'routes' }, | ||
]) | ||
|
||
useEffect(() => { | ||
if (!status.data) { | ||
return | ||
} | ||
|
||
setTabs((prevTabs) => | ||
prevTabs.map((tab) => { | ||
switch (tab.id) { | ||
case 'controllers': | ||
return { ...tab, count: status.data.controllers.length } | ||
case 'runners': | ||
return { ...tab, count: status.data.runners.length } | ||
case 'deployments': | ||
return { ...tab, count: status.data.deployments.length } | ||
case 'routes': | ||
return { ...tab, count: status.data.routes.length } | ||
default: | ||
return tab | ||
} | ||
}), | ||
) | ||
}, [status.data]) | ||
|
||
const handleTabClick = (tabId: string) => { | ||
setSearchParams({ tab: tabId }) | ||
} | ||
|
||
const currentTab = searchParams.get('tab') || tabs[0].id | ||
const renderTabContent = () => { | ||
switch (currentTab) { | ||
case 'controllers': | ||
return <ControllersList /> | ||
case 'runners': | ||
return <RunnersList /> | ||
case 'deployments': | ||
return <DeploymentsList /> | ||
case 'routes': | ||
return <RoutesList /> | ||
default: | ||
return <></> | ||
} | ||
} | ||
|
||
return ( | ||
<div className='px-6'> | ||
<Tabs tabs={tabs} initialTabId={currentTab} onTabClick={handleTabClick} /> | ||
<div className='mt-4'>{renderTabContent()}</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const RoutesList = () => { | ||
return <>Routes Content</> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const RunnersList = () => { | ||
return <>Runners Content</> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import type { StoryObj } from '@storybook/react/*' | ||
import { Pill } from '../components/Pill' | ||
|
||
const meta = { | ||
title: 'Components/Pill', | ||
component: Pill, | ||
} | ||
|
||
export default meta | ||
type Story = StoryObj<typeof meta> | ||
|
||
export const Primary: Story = { | ||
args: { | ||
text: 'name', | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import type { StoryObj } from '@storybook/react/*' | ||
import { Tabs } from '../components/Tabs' | ||
|
||
const meta = { | ||
title: 'Components/Tabs', | ||
component: Tabs, | ||
} | ||
|
||
export default meta | ||
type Story = StoryObj<typeof meta> | ||
|
||
export const Primary: Story = { | ||
args: { | ||
tabs: [ | ||
{ name: 'First Tab', id: 'first', count: 3 }, | ||
{ name: 'Second Tab', id: 'second', count: 1 }, | ||
{ name: 'Third Tab', id: 'third' }, | ||
], | ||
initialTabId: 'second', | ||
onTabClick: (tabId: string) => console.log(tabId), | ||
}, | ||
} |