This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Copy, ExternalLink, Link2, Plus } from 'lucide-react';
|
||||
import { Badge, Button, Card, CardHeader } from '../../components/ui';
|
||||
import { useToast } from '../../components/toast';
|
||||
@@ -74,6 +74,10 @@ function normalizeRepositoryName(repository: string) {
|
||||
return trimmed.includes('/') ? trimmed : `${giteaAppOrg}/${trimmed}`;
|
||||
}
|
||||
|
||||
function repositoryKey(repository: string) {
|
||||
return normalizeRepositoryName(repository).toLowerCase();
|
||||
}
|
||||
|
||||
function getHttpCloneUrl(repository: string) {
|
||||
return `${giteaBaseUrl.replace(/\/$/, '')}/${normalizeRepositoryName(repository)}.git`;
|
||||
}
|
||||
@@ -82,6 +86,16 @@ function getSshCloneUrl(repository: string) {
|
||||
return `${giteaSshBaseUrl.replace(/\/$/, '')}/${normalizeRepositoryName(repository)}.git`;
|
||||
}
|
||||
|
||||
function isSourceLinkComponent(source: SourceComponent) {
|
||||
return (source.imageSource ?? 'SOURCE') === 'SOURCE' && source.type !== 'DATABASE' && source.type !== 'MIGRATION';
|
||||
}
|
||||
|
||||
function isLauncherRepo(repo: PlatformRepo) {
|
||||
const name = repo.name.toLowerCase();
|
||||
const fullName = repo.fullName.toLowerCase();
|
||||
return name === 'studio-launcher' || name === 'studio-launcher-fe' || name === 'studio-launcher-be' || fullName.includes('/studio-launcher');
|
||||
}
|
||||
|
||||
export function SourceManagementPage() {
|
||||
const { toast } = useToast();
|
||||
const { apps, selectedAppId, loading: appsLoading, selectApp } = useApps();
|
||||
@@ -106,7 +120,7 @@ export function SourceManagementPage() {
|
||||
|
||||
const refreshRepos = () => {
|
||||
setGiteaStatus('loading');
|
||||
listGiteaRepos(giteaAppOrg, 8)
|
||||
listGiteaRepos(giteaAppOrg, 100)
|
||||
.then((repos) => {
|
||||
setGiteaRepos(repos.map(toPlatformRepo));
|
||||
setGiteaStatus('ready');
|
||||
@@ -130,7 +144,7 @@ export function SourceManagementPage() {
|
||||
setSourcesLoading(false);
|
||||
});
|
||||
|
||||
listGiteaRepos(giteaAppOrg, 8)
|
||||
listGiteaRepos(giteaAppOrg, 100)
|
||||
.then((repos) => {
|
||||
if (!mounted) return;
|
||||
setGiteaRepos(repos.map(toPlatformRepo));
|
||||
@@ -228,7 +242,22 @@ export function SourceManagementPage() {
|
||||
toast({ type: 'success', message: '복사되었습니다.' });
|
||||
};
|
||||
|
||||
const filteredSources = apiSources.filter((source) => !selectedAppId || source.appId === selectedAppId);
|
||||
const filteredSources = apiSources.filter((source) => isSourceLinkComponent(source) && (!selectedAppId || source.appId === selectedAppId));
|
||||
const selectedAppRepoKeys = useMemo(
|
||||
() => new Set(filteredSources.map((source) => source.repository).filter((repository): repository is string => Boolean(repository)).map(repositoryKey)),
|
||||
[filteredSources]
|
||||
);
|
||||
const visibleRepos = useMemo(
|
||||
() => giteaRepos
|
||||
.filter((repo) => !isLauncherRepo(repo))
|
||||
.sort((a, b) => {
|
||||
const aSelected = selectedAppRepoKeys.has(repositoryKey(a.fullName));
|
||||
const bSelected = selectedAppRepoKeys.has(repositoryKey(b.fullName));
|
||||
if (aSelected !== bSelected) return aSelected ? -1 : 1;
|
||||
return a.fullName.localeCompare(b.fullName);
|
||||
}),
|
||||
[giteaRepos, selectedAppRepoKeys]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -268,31 +297,35 @@ export function SourceManagementPage() {
|
||||
<tr>
|
||||
<td className="px-5 py-6 text-center text-muted-foreground" colSpan={6}>백엔드 연결 후 저장소가 표시됩니다.</td>
|
||||
</tr>
|
||||
) : giteaStatus !== 'loading' && giteaRepos.length === 0 ? (
|
||||
) : giteaStatus !== 'loading' && visibleRepos.length === 0 ? (
|
||||
<tr>
|
||||
<td className="px-5 py-6 text-center text-muted-foreground" colSpan={6}>등록된 저장소가 없습니다.</td>
|
||||
</tr>
|
||||
) : (
|
||||
giteaRepos.map((repo) => (
|
||||
<tr key={repo.id} className="hover:bg-muted/20">
|
||||
<td className="px-5 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-semibold">{repo.fullName}</span>
|
||||
<a href={repo.htmlUrl} target="_blank" rel="noreferrer" className="text-muted-foreground hover:text-foreground">
|
||||
<ExternalLink className="size-3.5" />
|
||||
</a>
|
||||
</div>
|
||||
{repo.description ? <p className="mt-1 text-xs text-muted-foreground">{repo.description}</p> : null}
|
||||
</td>
|
||||
<td className="px-5 py-3">{repo.owner}</td>
|
||||
<td className="px-5 py-3">{repo.defaultBranch}</td>
|
||||
<td className="px-5 py-3 text-muted-foreground">{repo.language || '-'}</td>
|
||||
<td className="px-5 py-3 font-mono text-xs text-muted-foreground">{repo.cloneUrl}</td>
|
||||
<td className="px-5 py-3">
|
||||
<Badge tone={repo.private ? 'amber' : 'blue'}>{repo.private ? 'Private' : 'Public'}</Badge>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
visibleRepos.map((repo) => {
|
||||
const isSelectedAppRepo = selectedAppRepoKeys.has(repositoryKey(repo.fullName));
|
||||
return (
|
||||
<tr key={repo.id} className="hover:bg-muted/20">
|
||||
<td className="px-5 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-semibold">{repo.fullName}</span>
|
||||
{isSelectedAppRepo ? <Badge tone="green">선택 앱</Badge> : null}
|
||||
<a href={repo.htmlUrl} target="_blank" rel="noreferrer" className="text-muted-foreground hover:text-foreground">
|
||||
<ExternalLink className="size-3.5" />
|
||||
</a>
|
||||
</div>
|
||||
{repo.description ? <p className="mt-1 text-xs text-muted-foreground">{repo.description}</p> : null}
|
||||
</td>
|
||||
<td className="px-5 py-3">{repo.owner}</td>
|
||||
<td className="px-5 py-3">{repo.defaultBranch}</td>
|
||||
<td className="px-5 py-3 text-muted-foreground">{repo.language || '-'}</td>
|
||||
<td className="px-5 py-3 font-mono text-xs text-muted-foreground">{repo.cloneUrl}</td>
|
||||
<td className="px-5 py-3">
|
||||
<Badge tone={repo.private ? 'amber' : 'blue'}>{repo.private ? 'Private' : 'Public'}</Badge>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -461,7 +494,7 @@ export function SourceManagementPage() {
|
||||
className="mt-2 h-9 w-full rounded-md border border-border bg-background px-3 text-sm outline-none focus:border-primary"
|
||||
>
|
||||
<option value="">저장소 선택</option>
|
||||
{giteaRepos.map((repo) => (
|
||||
{visibleRepos.map((repo) => (
|
||||
<option key={repo.id} value={repo.fullName}>{repo.fullName}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
Reference in New Issue
Block a user