Refactor git UI into reusable page components

This commit is contained in:
Chris Wanstrath 2026-03-01 09:39:02 -08:00
parent 35341600c1
commit 5e21323b54

View File

@ -96,11 +96,24 @@ const RepoName = define('RepoName', {
// Interfaces // Interfaces
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
interface AppRepoProps {
appName: string
baseUrl: string
branch: string
exists: boolean
commits: boolean
}
interface LayoutProps { interface LayoutProps {
title: string title: string
children: Child children: Child
} }
interface RepoListPageProps {
baseUrl: string
repos: Array<{ name: string; commits: boolean; branch: string }>
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Functions // Functions
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -400,6 +413,123 @@ async function withDeployLock<T>(repo: string, fn: () => Promise<T>): Promise<T>
} }
} }
function AppRepo({ appName, baseUrl, branch, exists, commits }: AppRepoProps) {
return (
<Layout title={`Git - ${appName}`}>
{exists && commits ? (
<>
<Heading>Repository</Heading>
<RepoList>
<RepoItem>
<div>
<RepoName>{appName}</RepoName>
<HelpText style="margin: 4px 0 0; font-size: 12px">
git clone {baseUrl}/{appName}.git
</HelpText>
</div>
<div style="display: flex; gap: 8px; align-items: center">
<Badge>{branch}</Badge>
<Badge style={`color: ${theme('colors-statusRunning')}`}>deployed</Badge>
</div>
</RepoItem>
</RepoList>
<Heading>Push Changes</Heading>
<CodeBlock>{[
`git push toes ${branch}`,
'',
'# Or if remote not yet added:',
`git remote add toes ${baseUrl}/${appName}.git`,
`git push toes ${branch}`,
].join('\n')}</CodeBlock>
</>
) : exists ? (
<>
<Heading>Repository</Heading>
<RepoList>
<RepoItem>
<div>
<RepoName>{appName}</RepoName>
<HelpText style="margin: 4px 0 0; font-size: 12px">
git clone {baseUrl}/{appName}.git
</HelpText>
</div>
<Badge>empty</Badge>
</RepoItem>
</RepoList>
<Heading>Push to Deploy</Heading>
<CodeBlock>{[
`git remote add toes ${baseUrl}/${appName}.git`,
'git push toes main',
].join('\n')}</CodeBlock>
</>
) : (
<>
<Heading>Push to Deploy</Heading>
<HelpText>
No git repository for <strong>{appName}</strong> yet.
Push to create one and deploy.
</HelpText>
<CodeBlock>{[
`git remote add toes ${baseUrl}/${appName}.git`,
'git push toes main',
].join('\n')}</CodeBlock>
</>
)}
</Layout>
)
}
function RepoListPage({ baseUrl, repos }: RepoListPageProps) {
return (
<Layout title="Git">
<Heading>Push to Deploy</Heading>
<HelpText>
Push a git repository to deploy it as a toes app.
The repo must contain a <code>package.json</code> with a <code>scripts.toes</code> entry.
</HelpText>
<CodeBlock>{[
'# Add this server as a remote and push',
`git remote add toes ${baseUrl}/<app-name>.git`,
'git push toes main',
'',
'# Or push an existing repo',
`git push ${baseUrl}/<app-name>.git main`,
].join('\n')}</CodeBlock>
{repos.length > 0 && (
<>
<Heading>Repositories</Heading>
<RepoList>
{repos.map(({ name, commits, branch }) => (
<RepoItem>
<div>
<RepoName>{name}</RepoName>
<HelpText style="margin: 4px 0 0; font-size: 12px">
git clone {baseUrl}/{name}.git
</HelpText>
</div>
<div style="display: flex; gap: 8px; align-items: center">
<Badge>{branch}</Badge>
{commits
? <Badge style={`color: ${theme('colors-statusRunning')}`}>deployed</Badge>
: <Badge>empty</Badge>}
</div>
</RepoItem>
))}
</RepoList>
</>
)}
{repos.length === 0 && (
<HelpText>No repositories yet. Push one to get started.</HelpText>
)}
</Layout>
)
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Module init // Module init
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -515,128 +645,18 @@ app.get('/', async c => {
? await Promise.all([hasCommits(bare), getDefaultBranch(bare)]) ? await Promise.all([hasCommits(bare), getDefaultBranch(bare)])
: [false, 'main'] : [false, 'main']
return c.html( return c.html(<AppRepo appName={appName} baseUrl={baseUrl} branch={branch} exists={exists} commits={commits} />)
<Layout title={`Git - ${appName}`}>
{exists && commits ? (
<>
<Heading>Repository</Heading>
<RepoList>
<RepoItem>
<div>
<RepoName>{appName}</RepoName>
<HelpText style="margin: 4px 0 0; font-size: 12px">
git clone {baseUrl}/{appName}.git
</HelpText>
</div>
<div style="display: flex; gap: 8px; align-items: center">
<Badge>{branch}</Badge>
<Badge style={`color: ${theme('colors-statusRunning')}`}>deployed</Badge>
</div>
</RepoItem>
</RepoList>
<Heading>Push Changes</Heading>
<CodeBlock>{[
`git push toes ${branch}`,
'',
'# Or if remote not yet added:',
`git remote add toes ${baseUrl}/${appName}.git`,
`git push toes ${branch}`,
].join('\n')}</CodeBlock>
</>
) : exists ? (
<>
<Heading>Repository</Heading>
<RepoList>
<RepoItem>
<div>
<RepoName>{appName}</RepoName>
<HelpText style="margin: 4px 0 0; font-size: 12px">
git clone {baseUrl}/{appName}.git
</HelpText>
</div>
<Badge>empty</Badge>
</RepoItem>
</RepoList>
<Heading>Push to Deploy</Heading>
<CodeBlock>{[
`git remote add toes ${baseUrl}/${appName}.git`,
'git push toes main',
].join('\n')}</CodeBlock>
</>
) : (
<>
<Heading>Push to Deploy</Heading>
<HelpText>
No git repository for <strong>{appName}</strong> yet.
Push to create one and deploy.
</HelpText>
<CodeBlock>{[
`git remote add toes ${baseUrl}/${appName}.git`,
'git push toes main',
].join('\n')}</CodeBlock>
</>
)}
</Layout>,
)
} }
// No app selected — show all repos // No app selected — show all repos
const repos = await listRepos() const repos = await listRepos()
const repoData = await Promise.all(repos.map(async name => { const repoData = await Promise.all(repos.map(async name => {
const bare = repoPath(name) const bare = repoPath(name)
const [commits, branch] = await Promise.all([hasCommits(bare), getDefaultBranch(bare)]) const [commits, branch] = await Promise.all([hasCommits(bare), getDefaultBranch(bare)])
return { name, commits, branch } return { name, commits, branch }
})) }))
return c.html( return c.html(<RepoListPage baseUrl={baseUrl} repos={repoData} />)
<Layout title="Git">
<Heading>Push to Deploy</Heading>
<HelpText>
Push a git repository to deploy it as a toes app.
The repo must contain a <code>package.json</code> with a <code>scripts.toes</code> entry.
</HelpText>
<CodeBlock>{[
'# Add this server as a remote and push',
`git remote add toes ${baseUrl}/<app-name>.git`,
'git push toes main',
'',
'# Or push an existing repo',
`git push ${baseUrl}/<app-name>.git main`,
].join('\n')}</CodeBlock>
{repoData.length > 0 && (
<>
<Heading>Repositories</Heading>
<RepoList>
{repoData.map(({ name, commits, branch }) => (
<RepoItem>
<div>
<RepoName>{name}</RepoName>
<HelpText style="margin: 4px 0 0; font-size: 12px">
git clone {baseUrl}/{name}.git
</HelpText>
</div>
<div style="display: flex; gap: 8px; align-items: center">
<Badge>{branch}</Badge>
{commits
? <Badge style={`color: ${theme('colors-statusRunning')}`}>deployed</Badge>
: <Badge>empty</Badge>}
</div>
</RepoItem>
))}
</RepoList>
</>
)}
{repoData.length === 0 && (
<HelpText>No repositories yet. Push one to get started.</HelpText>
)}
</Layout>,
)
}) })
export default app.defaults export default app.defaults