Added create site page

This commit is contained in:
2026-04-05 19:03:15 +02:00
parent a6a29461bc
commit a741d769bc
14 changed files with 870 additions and 19 deletions
+283
View File
@@ -0,0 +1,283 @@
import { useState } from 'react';
import Page from '../Page';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { Separator } from '@/components/ui/separator';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Plus, Zap } from 'lucide-react';
import { cn } from '@/lib/utils';
import { useCreateSite } from '../../hooks/api/useCreateSite';
const GIT_SERVERS = [
{
value: 'github',
label: 'GitHub',
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 cleaned = url
.trim()
.replace(/\.git$/, '')
.replace(/\/$/, '');
const patterns = [
/^https?:\/\/(github\.com|gitlab\.com)\/([^/]+)\/([^/]+)/,
/^git@(github\.com|gitlab\.com):([^/]+)\/([^/]+)/,
];
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] };
}
}
return null;
};
const NewSite = () => {
const [name, setName] = useState('');
const [repoUrl, setRepoUrl] = useState('');
const [gitServer, setGitServer] = useState('');
const [owner, setOwner] = useState('');
const [repository, setRepository] = useState('');
const [branch, setBranch] = useState('');
const [domain, setDomain] = useState('');
const [spa, setSpa] = useState(false);
const [urlError, setUrlError] = useState('');
const createNewSite = useCreateSite();
const handleQuickImport = () => {
if (!repoUrl.trim()) return;
const parsed = parseRepoUrl(repoUrl);
if (parsed) {
setGitServer(parsed.gitServer);
setOwner(parsed.owner);
setRepository(parsed.repository);
setUrlError('');
} else {
setUrlError(
'Could not parse URL. Supported formats: https://github.com/owner/repo or git@github.com:owner/repo'
);
}
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
handleQuickImport();
}
};
const handleSubmit = async () => {
console.log({ name, gitServer, owner, repository, branch, domain, spa });
const data = await createNewSite.mutateAsync({
name,
git_server: gitServer,
owner,
repository,
branch,
domain,
spa,
enabled: true,
});
console.log('Created site:', data);
};
const isValid = name && gitServer && owner && repository && branch && domain;
return (
<Page title="New Site">
<div className="">
<h1 className="text-2xl font-semibold mb-1">New Site</h1>
<p className="text-muted-foreground mb-6">
Deploy a static site from a Git repository.
</p>
<Card className="mb-6 bg-muted/70">
<CardHeader>
<CardTitle className="text-base">
<div className="flex items-center gap-1.5">
<Zap className="w-4 h-4" />
Quick Import
</div>
</CardTitle>
<CardDescription>
Paste a GitHub or GitLab repository URL to auto-fill the fields below.
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex gap-2">
<Input
placeholder="https://github.com/owner/repository"
value={repoUrl}
onChange={(e) => {
setRepoUrl(e.target.value);
setUrlError('');
}}
onKeyDown={handleKeyDown}
className="flex-1"
/>
<Button variant="outline" onClick={handleQuickImport}>
Import
</Button>
</div>
{urlError && <p className="text-sm text-destructive mt-2">{urlError}</p>}
</CardContent>
</Card>
<Separator className="mb-6" />
<div className="space-y-6">
<div className="space-y-2">
<Label htmlFor="name">Site Name</Label>
<Input
id="name"
placeholder="My awesome site"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<p className="text-sm text-muted-foreground">
A display name for your site. This can be changed later and is not part
of the URL.
</p>
</div>
<Separator />
<h2 className="text-lg font-medium">Repository</h2>
<div className="space-y-2">
<Label>Git Server</Label>
<div className="grid grid-cols-2 gap-3">
{GIT_SERVERS.map((server) => (
<button
key={server.value}
type="button"
onClick={() => setGitServer(server.value)}
className={cn(
'flex items-center gap-3 rounded-lg border-2 p-4 text-left transition-colors',
'hover:bg-accent/15',
gitServer === server.value
? 'border-primary bg-primary/5'
: 'border-border hover:border-accent/35'
)}
>
<div
className={cn(
'shrink-0',
gitServer === server.value
? 'text-primary'
: 'text-muted-foreground'
)}
>
{server.icon}
</div>
<span className="font-medium">{server.label}</span>
</button>
))}
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="owner">Owner</Label>
<Input
id="owner"
placeholder="owner"
value={owner}
onChange={(e) => setOwner(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="repository">Repository</Label>
<Input
id="repository"
placeholder="repository"
value={repository}
onChange={(e) => setRepository(e.target.value)}
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="branch">Branch</Label>
<Input
id="branch"
placeholder="gh-pages"
value={branch}
onChange={(e) => setBranch(e.target.value)}
/>
<p className="text-sm text-muted-foreground">
Assets from this branch will be deployed. Make sure it exists before
creating the site.
</p>
</div>
<Separator />
<h2 className="text-lg font-medium">Configuration</h2>
<div className="space-y-2">
<Label htmlFor="domain">Custom Domain</Label>
<Input
id="domain"
placeholder="example.com"
value={domain}
onChange={(e) => setDomain(e.target.value)}
/>
<p className="text-sm text-muted-foreground">
The domain you want to use for this site.
</p>
</div>
<div className="flex items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<Label htmlFor="spa">Single Page Application (SPA)</Label>
<p className="text-sm text-muted-foreground">
Redirect all paths to index.html for client-side routing.
</p>
</div>
<Switch id="spa" checked={spa} onCheckedChange={setSpa} />
</div>
<div className="flex gap-3 pt-2">
<Button
onClick={handleSubmit}
disabled={!isValid}
title={!isValid ? 'Please fill in all fields' : ''}
>
<Plus />
Create Site
</Button>
<Button variant="outline" onClick={() => window.history.back()}>
Cancel
</Button>
</div>
</div>
</div>
</Page>
);
};
export default NewSite;