From c3f4c9515084b35a2e2455e8c7b38ead54d8b0f6 Mon Sep 17 00:00:00 2001 From: KartoffelChipss Date: Sun, 3 May 2026 19:36:24 +0200 Subject: [PATCH] Add select for git servers when creating site --- frontend/src/components/GitServerTypeIcon.tsx | 20 ++ .../api/gitservers/useDeleteGitServer.ts | 3 + .../src/hooks/api/gitservers/useGitServer.ts | 9 + frontend/src/pages/GitServers/GitServers.tsx | 23 +- frontend/src/pages/NewSite/NewSite.tsx | 110 ++++--- .../src/pages/SiteOverview/OverviewTab.tsx | 275 ++++++++++-------- 6 files changed, 253 insertions(+), 187 deletions(-) create mode 100644 frontend/src/components/GitServerTypeIcon.tsx create mode 100644 frontend/src/hooks/api/gitservers/useGitServer.ts diff --git a/frontend/src/components/GitServerTypeIcon.tsx b/frontend/src/components/GitServerTypeIcon.tsx new file mode 100644 index 0000000..05b244d --- /dev/null +++ b/frontend/src/components/GitServerTypeIcon.tsx @@ -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 ; + case 'gitlab': + return ; + case 'gitea': + return ; + default: + return ; + } +}; + +export default GitServerTypeIcon; diff --git a/frontend/src/hooks/api/gitservers/useDeleteGitServer.ts b/frontend/src/hooks/api/gitservers/useDeleteGitServer.ts index dd63310..248e7c7 100644 --- a/frontend/src/hooks/api/gitservers/useDeleteGitServer.ts +++ b/frontend/src/hooks/api/gitservers/useDeleteGitServer.ts @@ -10,6 +10,9 @@ export function useDeleteGitServer() { qc.invalidateQueries({ queryKey: ['gitservers'], }); + qc.invalidateQueries({ + queryKey: ['gitServer'], + }); }, }); } diff --git a/frontend/src/hooks/api/gitservers/useGitServer.ts b/frontend/src/hooks/api/gitservers/useGitServer.ts new file mode 100644 index 0000000..68ca0cd --- /dev/null +++ b/frontend/src/hooks/api/gitservers/useGitServer.ts @@ -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), + }); +} diff --git a/frontend/src/pages/GitServers/GitServers.tsx b/frontend/src/pages/GitServers/GitServers.tsx index 32b182e..4f7bd3b 100644 --- a/frontend/src/pages/GitServers/GitServers.tsx +++ b/frontend/src/pages/GitServers/GitServers.tsx @@ -33,9 +33,7 @@ import { SelectTrigger, SelectValue, } from '../../components/ui/select'; -import { GiteaIcon } from '../../components/icons/GiteaIcon'; -import { GitLabIcon } from '../../components/icons/GitLabIcon'; -import { GitHubIcon } from '../../components/icons/GitHubIcon'; +import GitServerTypeIcon from '../../components/GitServerTypeIcon'; const DeleteGitServerDialog = ({ gitServerId }: { gitServerId: string }) => { const [open, setOpen] = useState(false); @@ -60,8 +58,8 @@ const DeleteGitServerDialog = ({ gitServerId }: { gitServerId: string }) => { Delete git server - Are you sure you want to delete this git server? This action cannot be - undone. + Are you sure you want to delete this git server? This will delete all + associated sites and cannot be undone. @@ -81,24 +79,11 @@ const DeleteGitServerDialog = ({ gitServerId }: { gitServerId: string }) => { ); }; -const GitServerTypeIcon = ({ type }: { type: GitServerType }) => { - switch (type) { - case 'github': - return ; - case 'gitlab': - return ; - case 'gitea': - return ; - default: - return ; - } -}; - const GitServerRow = ({ gitServer }: { gitServer: GitServer }) => { return (
- +

{gitServer.name}

diff --git a/frontend/src/pages/NewSite/NewSite.tsx b/frontend/src/pages/NewSite/NewSite.tsx index 82cc02a..bd778bd 100644 --- a/frontend/src/pages/NewSite/NewSite.tsx +++ b/frontend/src/pages/NewSite/NewSite.tsx @@ -17,47 +17,32 @@ import { import { Plus, Zap, Copy, Check, AlertTriangle } from 'lucide-react'; import { cn } from '@/lib/utils'; 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 = [ - { - value: 'github', - label: 'GitHub', - icon: ( - - - - ), - }, - { - value: 'gitlab', - label: 'GitLab', - icon: ( - - - - ), - }, -]; +interface ParsedRepo { + gitServer: string; + owner: string; + repository: string; +} -const parseRepoUrl = (url: string) => { +const parseRepoUrl = (url: string): ParsedRepo | null => { const cleaned = url .trim() .replace(/\.git$/, '') .replace(/\/$/, ''); - const patterns = [ - /^https?:\/\/(github\.com|gitlab\.com)\/([^/]+)\/([^/]+)/, - /^git@(github\.com|gitlab\.com):([^/]+)\/([^/]+)/, - ]; + // HTTPS: https://host/owner/repo + const httpsMatch = cleaned.match(/^https?:\/\/([^/]+)\/([^/]+)\/([^/]+)/); + if (httpsMatch) { + return { gitServer: httpsMatch[1], owner: httpsMatch[2], repository: httpsMatch[3] }; + } - for (const pattern of patterns) { - const match = cleaned.match(pattern); - if (match) { - const host = match[1]; - const server = host === 'github.com' ? 'github' : 'gitlab'; - return { gitServer: server, owner: match[2], repository: match[3] }; - } + // SSH: git@host:owner/repo + const sshMatch = cleaned.match(/^git@([^:]+):([^/]+)\/([^/]+)/); + if (sshMatch) { + return { gitServer: sshMatch[1], owner: sshMatch[2], repository: sshMatch[3] }; } return null; @@ -80,13 +65,28 @@ const NewSite = () => { const [copiedToken, setCopiedToken] = useState(false); const [copiedCurl, setCopiedCurl] = useState(false); const createNewSite = useCreateSite(); + const { + data: gitServers, + isLoading: isLoadingGitServers, + error: gitServersError, + } = useGitServers(); const handleQuickImport = () => { if (!repoUrl.trim()) return; const parsed = parseRepoUrl(repoUrl); 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); setRepository(parsed.repository); setUrlError(''); @@ -209,15 +209,15 @@ const NewSite = () => {
- {GIT_SERVERS.map((server) => ( + {gitServers?.map((server) => ( ))} + {!isLoadingGitServers && + !gitServersError && + gitServers?.length === 0 && ( +
+ No git servers found. Please add a git server first.{' '} + + Add Git Server + +
+ )} + {isLoadingGitServers && ( +
+ Loading git servers... +
+ )} + {gitServersError && ( +
+ Error loading git servers +
+ )}
diff --git a/frontend/src/pages/SiteOverview/OverviewTab.tsx b/frontend/src/pages/SiteOverview/OverviewTab.tsx index 785cf09..6b097f1 100644 --- a/frontend/src/pages/SiteOverview/OverviewTab.tsx +++ b/frontend/src/pages/SiteOverview/OverviewTab.tsx @@ -19,138 +19,159 @@ import { } from '../../components/ui/table'; import type { Deployment } from '../../api/types/deployments'; import DeploymentStatusBadge from '../../components/DeploymentStatusBadge'; +import { useGitServer } from '../../hooks/api/gitservers/useGitServer'; +import type { GitServer } from '../../api/types/gitserver'; -const repoUrl = (site: Site) => { - const host = site.git_server === 'github' ? 'github.com' : 'gitlab.com'; - return `https://${host}/${site.owner}/${site.repository}`; +const repoUrl = (site: Site, gitServer: GitServer | null) => { + if (!gitServer) return 'man'; + const baseUrl = gitServer.baseUrl.replace(/\/+$/, ''); // Remove trailing slashes + return `${gitServer.protocol}://${baseUrl}/${site.owner}/${site.repository}`; }; -const OverviewTab = ({ site, deployments }: { site: Site; deployments?: Deployment[] }) => ( -
- - - Repository - - -
- Provider - {site.git_server} -
- - - -
- Branch - - - {site.branch} - -
-
-
+const OverviewTab = ({ site, deployments }: { site: Site; deployments?: Deployment[] }) => { + const { + data: gitServer, + isLoading: isGitServerLoading, + error: gitServerError, + } = useGitServer(site.git_server); - - - Configuration - - - - -
- SPA Mode -
- {site.spa ? : } - {site.spa ? 'Enabled' : 'Disabled'} + return ( +
+ + + Repository + + +
+ Provider + {isGitServerLoading ? ( + Loading... + ) : gitServerError ? ( + Error loading git server + ) : ( + {gitServer?.name} + )}
-
- -
- Last Deployed - - {deployments - ? formatDateRelativeOrAbsolute(deployments[0]?.created_at) - : '—'} - -
- - + + + +
+ Branch + + + {site.branch} + +
+ + - - - Recent Deployments - Last 3 deployments for this site. - - - {deployments && deployments.length > 0 && ( - - - - Status - Commit - Message - Duration - Time - - - - {deployments.slice(0, 3).map((d) => { - const githubCommitUrl = `https://github.com/${site.owner}/${site.repository}/commit/${d.commit_hash}`; - return ( - - - - - - - {d.commit_hash.substring(0, 7)} - - - - {d.message} - - - {d.start_time && d.finish_time - ? formatDuration(d.start_time, d.finish_time, false) - : '—'} - - - {formatDateRelativeOrAbsolute(d.created_at)} - - - ); - })} - -
- )} -
-
-
-); + + + Configuration + + + + +
+ SPA Mode +
+ {site.spa ? : } + {site.spa ? 'Enabled' : 'Disabled'} +
+
+ +
+ Last Deployed + + {deployments + ? formatDateRelativeOrAbsolute(deployments[0]?.created_at) + : '—'} + +
+
+
+ + + + Recent Deployments + Last 3 deployments for this site. + + + {deployments && deployments.length > 0 && ( + + + + Status + Commit + Message + Duration + Time + + + + {deployments.slice(0, 3).map((d) => { + const githubCommitUrl = `https://github.com/${site.owner}/${site.repository}/commit/${d.commit_hash}`; + return ( + + + + + + + {d.commit_hash.substring(0, 7)} + + + + {d.message} + + + {d.start_time && d.finish_time + ? formatDuration( + d.start_time, + d.finish_time, + false + ) + : '—'} + + + {formatDateRelativeOrAbsolute(d.created_at)} + + + ); + })} + +
+ )} +
+
+
+ ); +}; export default OverviewTab;