can now import repos and have more settings for creating new ones.

This commit is contained in:
2026-05-07 12:16:58 +02:00
parent dad82a79de
commit 39dd9ab9eb
99 changed files with 7442 additions and 131 deletions
+27 -75
View File
@@ -1,12 +1,10 @@
import { useState } from 'react'
import { useRepos, useCreateRepo } from '../api/queries/repos'
import { useRepos } from '../api/queries/repos'
import { RepoCard } from '../components/repos/RepoCard'
import { RepoListSkeleton } from '../ui/Skeleton'
import { Link } from 'react-router-dom'
export default function ReposPage() {
const { data: repos, isLoading, isError } = useRepos()
const [showCreate, setShowCreate] = useState(false)
return (
<div className="max-w-4xl mx-auto px-4 md:px-6 py-6">
@@ -17,27 +15,39 @@ export default function ReposPage() {
<p className="text-sm text-[#5E6C84] mt-0.5">{repos.length} repositor{repos.length === 1 ? 'y' : 'ies'}</p>
)}
</div>
<button
onClick={() => setShowCreate(true)}
className="flex items-center gap-2 px-4 py-2 rounded bg-[#0052CC] text-white text-sm font-medium hover:bg-[#0065FF] min-h-[44px]"
>
<svg width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
New
</button>
<div className="flex items-center gap-2">
<Link
to="/repos/import"
className="flex items-center gap-1.5 px-3 py-2 rounded border border-[#DFE1E6] text-sm text-[#172B4D] hover:bg-[#F4F5F7] font-medium min-h-[36px]"
>
Import
</Link>
<Link
to="/repos/new"
className="flex items-center gap-2 px-4 py-2 rounded bg-[#0052CC] text-white text-sm font-medium hover:bg-[#0065FF] min-h-[36px]"
>
<svg width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2.5" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
New repository
</Link>
</div>
</div>
{showCreate && <CreateRepoForm onClose={() => setShowCreate(false)} />}
{isLoading ? (
<RepoListSkeleton />
) : isError ? (
<NotSignedIn />
) : !repos?.length ? (
<div className="py-12 text-center text-sm text-[#5E6C84]">
No repositories yet.{' '}
<button onClick={() => setShowCreate(true)} className="text-[#0052CC] hover:underline">Create your first one.</button>
<div className="py-16 text-center">
<svg width="48" height="48" fill="none" stroke="#97A0AF" strokeWidth="1" viewBox="0 0 24 24" className="mx-auto mb-4">
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 0 0-1.883 2.542l.857 6a2.25 2.25 0 0 0 2.227 1.932H19.05a2.25 2.25 0 0 0 2.227-1.932l.857-6a2.25 2.25 0 0 0-1.883-2.542m-16.5 0V6A2.25 2.25 0 0 1 6 3.75h3.879a1.5 1.5 0 0 1 1.06.44l2.122 2.12a1.5 1.5 0 0 0 1.06.44H18A2.25 2.25 0 0 1 20.25 9v.776" />
</svg>
<p className="text-sm font-medium text-[#172B4D] mb-1">No repositories yet</p>
<p className="text-xs text-[#5E6C84] mb-4">Create your first repository to get started.</p>
<Link to="/repos/new" className="px-4 py-2 rounded bg-[#0052CC] text-white text-sm font-medium hover:bg-[#0065FF]">
Create repository
</Link>
</div>
) : (
<div className="flex flex-col gap-2">
@@ -64,61 +74,3 @@ function NotSignedIn() {
</div>
)
}
function CreateRepoForm({ onClose }: { onClose: () => void }) {
const createRepo = useCreateRepo()
const [name, setName] = useState('')
const [description, setDescription] = useState('')
const [isPrivate, setIsPrivate] = useState(false)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!name.trim()) return
await createRepo.mutateAsync({ name: name.trim(), description, isPrivate })
onClose()
}
return (
<div className="mb-6 p-5 border border-[#4C9AFF] rounded bg-white shadow-sm">
<h2 className="text-sm font-semibold text-[#172B4D] mb-4">New Repository</h2>
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
<div>
<label className="block text-xs font-medium text-[#172B4D] mb-1">Name *</label>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="my-project"
required
className="w-full border border-[#DFE1E6] rounded px-3 py-2 text-sm focus:outline-none focus:border-[#4C9AFF]"
/>
</div>
<div>
<label className="block text-xs font-medium text-[#172B4D] mb-1">Description</label>
<input
value={description}
onChange={e => setDescription(e.target.value)}
placeholder="Optional"
className="w-full border border-[#DFE1E6] rounded px-3 py-2 text-sm focus:outline-none focus:border-[#4C9AFF]"
/>
</div>
<label className="flex items-center gap-2 cursor-pointer">
<input type="checkbox" checked={isPrivate} onChange={e => setIsPrivate(e.target.checked)} />
<span className="text-sm text-[#172B4D]">Private</span>
</label>
{createRepo.isError && (
<p className="text-xs text-[#DE350B]">{createRepo.error instanceof Error ? createRepo.error.message : 'Error'}</p>
)}
<div className="flex gap-2">
<button type="submit" disabled={createRepo.isPending || !name.trim()}
className="px-4 py-2 rounded bg-[#0052CC] text-white text-sm font-medium hover:bg-[#0065FF] disabled:opacity-50 min-h-[44px]">
{createRepo.isPending ? 'Creating…' : 'Create'}
</button>
<button type="button" onClick={onClose}
className="px-4 py-2 rounded border border-[#DFE1E6] text-sm text-[#172B4D] hover:bg-[#F4F5F7] min-h-[44px]">
Cancel
</button>
</div>
</form>
</div>
)
}