Add select for git servers when creating site
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
import { Code2 } from 'lucide-react';
|
||||||
|
import type { GitServerType } from '../api/types/gitserver';
|
||||||
|
import { GiteaIcon } from './icons/GiteaIcon';
|
||||||
|
import { GitHubIcon } from './icons/GitHubIcon';
|
||||||
|
import { GitLabIcon } from './icons/GitLabIcon';
|
||||||
|
|
||||||
|
const GitServerTypeIcon = ({ type, size = 32 }: { type: GitServerType; size?: number }) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'github':
|
||||||
|
return <GitHubIcon size={size} />;
|
||||||
|
case 'gitlab':
|
||||||
|
return <GitLabIcon size={size} />;
|
||||||
|
case 'gitea':
|
||||||
|
return <GiteaIcon size={size} />;
|
||||||
|
default:
|
||||||
|
return <Code2 />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GitServerTypeIcon;
|
||||||
@@ -10,6 +10,9 @@ export function useDeleteGitServer() {
|
|||||||
qc.invalidateQueries({
|
qc.invalidateQueries({
|
||||||
queryKey: ['gitservers'],
|
queryKey: ['gitservers'],
|
||||||
});
|
});
|
||||||
|
qc.invalidateQueries({
|
||||||
|
queryKey: ['gitServer'],
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { getGitServerById } from '../../../api/gitservers.api';
|
||||||
|
|
||||||
|
export function useGitServer(id: string) {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['gitServer', id],
|
||||||
|
queryFn: () => getGitServerById(id),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -33,9 +33,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '../../components/ui/select';
|
} from '../../components/ui/select';
|
||||||
import { GiteaIcon } from '../../components/icons/GiteaIcon';
|
import GitServerTypeIcon from '../../components/GitServerTypeIcon';
|
||||||
import { GitLabIcon } from '../../components/icons/GitLabIcon';
|
|
||||||
import { GitHubIcon } from '../../components/icons/GitHubIcon';
|
|
||||||
|
|
||||||
const DeleteGitServerDialog = ({ gitServerId }: { gitServerId: string }) => {
|
const DeleteGitServerDialog = ({ gitServerId }: { gitServerId: string }) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -60,8 +58,8 @@ const DeleteGitServerDialog = ({ gitServerId }: { gitServerId: string }) => {
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Delete git server</DialogTitle>
|
<DialogTitle>Delete git server</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Are you sure you want to delete this git server? This action cannot be
|
Are you sure you want to delete this git server? This will delete all
|
||||||
undone.
|
associated sites and cannot be undone.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
@@ -81,24 +79,11 @@ const DeleteGitServerDialog = ({ gitServerId }: { gitServerId: string }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const GitServerTypeIcon = ({ type }: { type: GitServerType }) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'github':
|
|
||||||
return <GitHubIcon size={32} />;
|
|
||||||
case 'gitlab':
|
|
||||||
return <GitLabIcon size={32} />;
|
|
||||||
case 'gitea':
|
|
||||||
return <GiteaIcon size={32} />;
|
|
||||||
default:
|
|
||||||
return <Code2 />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const GitServerRow = ({ gitServer }: { gitServer: GitServer }) => {
|
const GitServerRow = ({ gitServer }: { gitServer: GitServer }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-4 p-4 rounded-lg border">
|
<div className="flex items-center gap-4 p-4 rounded-lg border">
|
||||||
<div className="text-2xl">
|
<div className="text-2xl">
|
||||||
<GitServerTypeIcon type={gitServer.type} />
|
<GitServerTypeIcon type={gitServer.type} size={32} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<p className="font-medium">{gitServer.name}</p>
|
<p className="font-medium">{gitServer.name}</p>
|
||||||
|
|||||||
@@ -17,47 +17,32 @@ import {
|
|||||||
import { Plus, Zap, Copy, Check, AlertTriangle } from 'lucide-react';
|
import { Plus, Zap, Copy, Check, AlertTriangle } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useCreateSite } from '../../hooks/api/useCreateSite';
|
import { useCreateSite } from '../../hooks/api/useCreateSite';
|
||||||
import { useNavigate } from 'react-router';
|
import { Link, useNavigate } from 'react-router';
|
||||||
|
import GitServerTypeIcon from '../../components/GitServerTypeIcon';
|
||||||
|
import { useGitServers } from '../../hooks/api/gitservers/useGitServers';
|
||||||
|
|
||||||
const GIT_SERVERS = [
|
interface ParsedRepo {
|
||||||
{
|
gitServer: string;
|
||||||
value: 'github',
|
owner: string;
|
||||||
label: 'GitHub',
|
repository: string;
|
||||||
icon: (
|
}
|
||||||
<svg viewBox="0 0 24 24" className="w-6 h-6 fill-current">
|
|
||||||
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'gitlab',
|
|
||||||
label: 'GitLab',
|
|
||||||
icon: (
|
|
||||||
<svg viewBox="0 0 24 24" className="w-6 h-6 fill-current">
|
|
||||||
<path d="M23.955 13.587l-1.342-4.135-2.664-8.189a.455.455 0 00-.867 0L16.418 9.45H7.582L4.918 1.263a.455.455 0 00-.867 0L1.387 9.452.045 13.587a.924.924 0 00.331 1.023L12 23.054l11.624-8.443a.92.92 0 00.331-1.024" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const parseRepoUrl = (url: string) => {
|
const parseRepoUrl = (url: string): ParsedRepo | null => {
|
||||||
const cleaned = url
|
const cleaned = url
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/\.git$/, '')
|
.replace(/\.git$/, '')
|
||||||
.replace(/\/$/, '');
|
.replace(/\/$/, '');
|
||||||
|
|
||||||
const patterns = [
|
// HTTPS: https://host/owner/repo
|
||||||
/^https?:\/\/(github\.com|gitlab\.com)\/([^/]+)\/([^/]+)/,
|
const httpsMatch = cleaned.match(/^https?:\/\/([^/]+)\/([^/]+)\/([^/]+)/);
|
||||||
/^git@(github\.com|gitlab\.com):([^/]+)\/([^/]+)/,
|
if (httpsMatch) {
|
||||||
];
|
return { gitServer: httpsMatch[1], owner: httpsMatch[2], repository: httpsMatch[3] };
|
||||||
|
}
|
||||||
|
|
||||||
for (const pattern of patterns) {
|
// SSH: git@host:owner/repo
|
||||||
const match = cleaned.match(pattern);
|
const sshMatch = cleaned.match(/^git@([^:]+):([^/]+)\/([^/]+)/);
|
||||||
if (match) {
|
if (sshMatch) {
|
||||||
const host = match[1];
|
return { gitServer: sshMatch[1], owner: sshMatch[2], repository: sshMatch[3] };
|
||||||
const server = host === 'github.com' ? 'github' : 'gitlab';
|
|
||||||
return { gitServer: server, owner: match[2], repository: match[3] };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -80,13 +65,28 @@ const NewSite = () => {
|
|||||||
const [copiedToken, setCopiedToken] = useState(false);
|
const [copiedToken, setCopiedToken] = useState(false);
|
||||||
const [copiedCurl, setCopiedCurl] = useState(false);
|
const [copiedCurl, setCopiedCurl] = useState(false);
|
||||||
const createNewSite = useCreateSite();
|
const createNewSite = useCreateSite();
|
||||||
|
const {
|
||||||
|
data: gitServers,
|
||||||
|
isLoading: isLoadingGitServers,
|
||||||
|
error: gitServersError,
|
||||||
|
} = useGitServers();
|
||||||
|
|
||||||
const handleQuickImport = () => {
|
const handleQuickImport = () => {
|
||||||
if (!repoUrl.trim()) return;
|
if (!repoUrl.trim()) return;
|
||||||
|
|
||||||
const parsed = parseRepoUrl(repoUrl);
|
const parsed = parseRepoUrl(repoUrl);
|
||||||
if (parsed) {
|
if (parsed) {
|
||||||
setGitServer(parsed.gitServer);
|
const gitServerEntry = gitServers?.find((server) =>
|
||||||
|
server.baseUrl.includes(parsed.gitServer)
|
||||||
|
);
|
||||||
|
if (gitServerEntry) {
|
||||||
|
setGitServer(gitServerEntry.id);
|
||||||
|
} else {
|
||||||
|
setUrlError(
|
||||||
|
'Git server not found. Please make sure the git server is added to the app.'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setOwner(parsed.owner);
|
setOwner(parsed.owner);
|
||||||
setRepository(parsed.repository);
|
setRepository(parsed.repository);
|
||||||
setUrlError('');
|
setUrlError('');
|
||||||
@@ -209,15 +209,15 @@ const NewSite = () => {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Git Server</Label>
|
<Label>Git Server</Label>
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
{GIT_SERVERS.map((server) => (
|
{gitServers?.map((server) => (
|
||||||
<button
|
<button
|
||||||
key={server.value}
|
key={server.id}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setGitServer(server.value)}
|
onClick={() => setGitServer(server.id)}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center gap-3 rounded-lg border-2 p-4 text-left transition-colors',
|
'flex items-center gap-3 rounded-lg border-2 p-4 text-left transition-colors',
|
||||||
'hover:bg-accent/15',
|
'hover:bg-accent/15',
|
||||||
gitServer === server.value
|
gitServer === server.id
|
||||||
? 'border-primary bg-primary/5'
|
? 'border-primary bg-primary/5'
|
||||||
: 'border-border hover:border-accent/35'
|
: 'border-border hover:border-accent/35'
|
||||||
)}
|
)}
|
||||||
@@ -225,16 +225,44 @@ const NewSite = () => {
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'shrink-0',
|
'shrink-0',
|
||||||
gitServer === server.value
|
gitServer === server.id
|
||||||
? 'text-primary'
|
? 'text-primary'
|
||||||
: 'text-muted-foreground'
|
: 'text-muted-foreground'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{server.icon}
|
<GitServerTypeIcon type={server.type} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-medium">{server.name}</span>
|
||||||
|
<span className="block text-sm text-muted-foreground">
|
||||||
|
{server.baseUrl}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="font-medium">{server.label}</span>
|
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
{!isLoadingGitServers &&
|
||||||
|
!gitServersError &&
|
||||||
|
gitServers?.length === 0 && (
|
||||||
|
<div className="col-span-2 text-muted-foreground">
|
||||||
|
No git servers found. Please add a git server first.{' '}
|
||||||
|
<Link
|
||||||
|
to="/gitservers"
|
||||||
|
className="text-primary hover:underline"
|
||||||
|
>
|
||||||
|
Add Git Server
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isLoadingGitServers && (
|
||||||
|
<div className="col-span-2 flex items-center justify-center p-4">
|
||||||
|
Loading git servers...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{gitServersError && (
|
||||||
|
<div className="col-span-2 flex items-center justify-center p-4 text-destructive">
|
||||||
|
Error loading git servers
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -19,138 +19,159 @@ import {
|
|||||||
} from '../../components/ui/table';
|
} from '../../components/ui/table';
|
||||||
import type { Deployment } from '../../api/types/deployments';
|
import type { Deployment } from '../../api/types/deployments';
|
||||||
import DeploymentStatusBadge from '../../components/DeploymentStatusBadge';
|
import DeploymentStatusBadge from '../../components/DeploymentStatusBadge';
|
||||||
|
import { useGitServer } from '../../hooks/api/gitservers/useGitServer';
|
||||||
|
import type { GitServer } from '../../api/types/gitserver';
|
||||||
|
|
||||||
const repoUrl = (site: Site) => {
|
const repoUrl = (site: Site, gitServer: GitServer | null) => {
|
||||||
const host = site.git_server === 'github' ? 'github.com' : 'gitlab.com';
|
if (!gitServer) return 'man';
|
||||||
return `https://${host}/${site.owner}/${site.repository}`;
|
const baseUrl = gitServer.baseUrl.replace(/\/+$/, ''); // Remove trailing slashes
|
||||||
|
return `${gitServer.protocol}://${baseUrl}/${site.owner}/${site.repository}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const OverviewTab = ({ site, deployments }: { site: Site; deployments?: Deployment[] }) => (
|
const OverviewTab = ({ site, deployments }: { site: Site; deployments?: Deployment[] }) => {
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
const {
|
||||||
<Card>
|
data: gitServer,
|
||||||
<CardHeader>
|
isLoading: isGitServerLoading,
|
||||||
<CardTitle className="text-base">Repository</CardTitle>
|
error: gitServerError,
|
||||||
</CardHeader>
|
} = useGitServer(site.git_server);
|
||||||
<CardContent className="space-y-3 text-sm">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="text-muted-foreground">Provider</span>
|
|
||||||
<span className="capitalize">{site.git_server}</span>
|
|
||||||
</div>
|
|
||||||
<Separator />
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="text-muted-foreground">Owner / Repo</span>
|
|
||||||
<a
|
|
||||||
href={repoUrl(site)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="inline-flex items-center gap-1 text-primary hover:underline"
|
|
||||||
>
|
|
||||||
{site.owner}/{site.repository}
|
|
||||||
<ExternalLink className="w-3 h-3" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<Separator />
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="text-muted-foreground">Branch</span>
|
|
||||||
<span className="inline-flex items-center gap-1">
|
|
||||||
<GitBranch className="w-3 h-3" />
|
|
||||||
{site.branch}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
return (
|
||||||
<CardHeader>
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
<CardTitle className="text-base">Configuration</CardTitle>
|
<Card>
|
||||||
</CardHeader>
|
<CardHeader>
|
||||||
<CardContent className="space-y-3 text-sm">
|
<CardTitle className="text-base">Repository</CardTitle>
|
||||||
<div className="flex justify-between">
|
</CardHeader>
|
||||||
<span className="text-muted-foreground">Domain</span>
|
<CardContent className="space-y-3 text-sm">
|
||||||
<a
|
<div className="flex justify-between">
|
||||||
href={`https://${site.domain}`}
|
<span className="text-muted-foreground">Provider</span>
|
||||||
target="_blank"
|
{isGitServerLoading ? (
|
||||||
rel="noopener noreferrer"
|
<span>Loading...</span>
|
||||||
className="inline-flex items-center gap-1 text-primary hover:underline"
|
) : gitServerError ? (
|
||||||
>
|
<span>Error loading git server</span>
|
||||||
{site.domain}
|
) : (
|
||||||
<ExternalLink className="w-3 h-3" />
|
<span>{gitServer?.name}</span>
|
||||||
</a>
|
)}
|
||||||
</div>
|
|
||||||
<Separator />
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="text-muted-foreground">SPA Mode</span>
|
|
||||||
<div className="inline-flex items-center gap-1">
|
|
||||||
{site.spa ? <Check className="w-4 h-4" /> : <X className="w-4 h-4" />}
|
|
||||||
<span>{site.spa ? 'Enabled' : 'Disabled'}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<Separator />
|
||||||
<Separator />
|
<div className="flex justify-between">
|
||||||
<div className="flex justify-between">
|
<span className="text-muted-foreground">Owner / Repo</span>
|
||||||
<span className="text-muted-foreground">Last Deployed</span>
|
<a
|
||||||
<span>
|
href={repoUrl(site, gitServer || null)}
|
||||||
{deployments
|
target="_blank"
|
||||||
? formatDateRelativeOrAbsolute(deployments[0]?.created_at)
|
rel="noopener noreferrer"
|
||||||
: '—'}
|
className="inline-flex items-center gap-1 text-primary hover:underline"
|
||||||
</span>
|
>
|
||||||
</div>
|
{site.owner}/{site.repository}
|
||||||
</CardContent>
|
<ExternalLink className="w-3 h-3" />
|
||||||
</Card>
|
</a>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-muted-foreground">Branch</span>
|
||||||
|
<span className="inline-flex items-center gap-1">
|
||||||
|
<GitBranch className="w-3 h-3" />
|
||||||
|
{site.branch}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Card className="md:col-span-2">
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-base">Recent Deployments</CardTitle>
|
<CardTitle className="text-base">Configuration</CardTitle>
|
||||||
<CardDescription>Last 3 deployments for this site.</CardDescription>
|
</CardHeader>
|
||||||
</CardHeader>
|
<CardContent className="space-y-3 text-sm">
|
||||||
<CardContent>
|
<div className="flex justify-between">
|
||||||
{deployments && deployments.length > 0 && (
|
<span className="text-muted-foreground">Domain</span>
|
||||||
<Table>
|
<a
|
||||||
<TableHeader>
|
href={`https://${site.domain}`}
|
||||||
<TableRow>
|
target="_blank"
|
||||||
<TableHead>Status</TableHead>
|
rel="noopener noreferrer"
|
||||||
<TableHead>Commit</TableHead>
|
className="inline-flex items-center gap-1 text-primary hover:underline"
|
||||||
<TableHead className="hidden sm:table-cell">Message</TableHead>
|
>
|
||||||
<TableHead className="text-right">Duration</TableHead>
|
{site.domain}
|
||||||
<TableHead className="text-right">Time</TableHead>
|
<ExternalLink className="w-3 h-3" />
|
||||||
</TableRow>
|
</a>
|
||||||
</TableHeader>
|
</div>
|
||||||
<TableBody>
|
<Separator />
|
||||||
{deployments.slice(0, 3).map((d) => {
|
<div className="flex justify-between">
|
||||||
const githubCommitUrl = `https://github.com/${site.owner}/${site.repository}/commit/${d.commit_hash}`;
|
<span className="text-muted-foreground">SPA Mode</span>
|
||||||
return (
|
<div className="inline-flex items-center gap-1">
|
||||||
<TableRow key={d.id}>
|
{site.spa ? <Check className="w-4 h-4" /> : <X className="w-4 h-4" />}
|
||||||
<TableCell>
|
<span>{site.spa ? 'Enabled' : 'Disabled'}</span>
|
||||||
<DeploymentStatusBadge status={d.status} />
|
</div>
|
||||||
</TableCell>
|
</div>
|
||||||
<TableCell className="font-mono text-xs">
|
<Separator />
|
||||||
<a
|
<div className="flex justify-between">
|
||||||
href={githubCommitUrl}
|
<span className="text-muted-foreground">Last Deployed</span>
|
||||||
target="_blank"
|
<span>
|
||||||
rel="noopener noreferrer"
|
{deployments
|
||||||
>
|
? formatDateRelativeOrAbsolute(deployments[0]?.created_at)
|
||||||
{d.commit_hash.substring(0, 7)}
|
: '—'}
|
||||||
</a>
|
</span>
|
||||||
</TableCell>
|
</div>
|
||||||
<TableCell className="hidden sm:table-cell text-muted-foreground">
|
</CardContent>
|
||||||
{d.message}
|
</Card>
|
||||||
</TableCell>
|
|
||||||
<TableCell className="text-right text-muted-foreground">
|
<Card className="md:col-span-2">
|
||||||
{d.start_time && d.finish_time
|
<CardHeader>
|
||||||
? formatDuration(d.start_time, d.finish_time, false)
|
<CardTitle className="text-base">Recent Deployments</CardTitle>
|
||||||
: '—'}
|
<CardDescription>Last 3 deployments for this site.</CardDescription>
|
||||||
</TableCell>
|
</CardHeader>
|
||||||
<TableCell className="text-right text-muted-foreground">
|
<CardContent>
|
||||||
{formatDateRelativeOrAbsolute(d.created_at)}
|
{deployments && deployments.length > 0 && (
|
||||||
</TableCell>
|
<Table>
|
||||||
</TableRow>
|
<TableHeader>
|
||||||
);
|
<TableRow>
|
||||||
})}
|
<TableHead>Status</TableHead>
|
||||||
</TableBody>
|
<TableHead>Commit</TableHead>
|
||||||
</Table>
|
<TableHead className="hidden sm:table-cell">Message</TableHead>
|
||||||
)}
|
<TableHead className="text-right">Duration</TableHead>
|
||||||
</CardContent>
|
<TableHead className="text-right">Time</TableHead>
|
||||||
</Card>
|
</TableRow>
|
||||||
</div>
|
</TableHeader>
|
||||||
);
|
<TableBody>
|
||||||
|
{deployments.slice(0, 3).map((d) => {
|
||||||
|
const githubCommitUrl = `https://github.com/${site.owner}/${site.repository}/commit/${d.commit_hash}`;
|
||||||
|
return (
|
||||||
|
<TableRow key={d.id}>
|
||||||
|
<TableCell>
|
||||||
|
<DeploymentStatusBadge status={d.status} />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="font-mono text-xs">
|
||||||
|
<a
|
||||||
|
href={githubCommitUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{d.commit_hash.substring(0, 7)}
|
||||||
|
</a>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden sm:table-cell text-muted-foreground">
|
||||||
|
{d.message}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-right text-muted-foreground">
|
||||||
|
{d.start_time && d.finish_time
|
||||||
|
? formatDuration(
|
||||||
|
d.start_time,
|
||||||
|
d.finish_time,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
: '—'}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-right text-muted-foreground">
|
||||||
|
{formatDateRelativeOrAbsolute(d.created_at)}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default OverviewTab;
|
export default OverviewTab;
|
||||||
|
|||||||
Reference in New Issue
Block a user