refine app management and source repository displays
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -73,12 +73,12 @@ function getTone(status: string) {
|
||||
return 'neutral';
|
||||
}
|
||||
|
||||
function formatStatus(status?: string | null) {
|
||||
function formatStatus(status?: string | null, launcherLinked?: boolean) {
|
||||
if (status === 'DISABLED' || status === 'INACTIVE') return '비활성';
|
||||
if (launcherLinked) return '활성';
|
||||
const statusMap: Record<string, string> = {
|
||||
ACTIVE: '준비됨',
|
||||
ACTIVE: '활성',
|
||||
READY: '준비됨',
|
||||
INACTIVE: '비활성',
|
||||
DISABLED: '비활성',
|
||||
ERROR: '오류'
|
||||
};
|
||||
|
||||
@@ -906,7 +906,9 @@ export function AppManagementPage() {
|
||||
<td className="px-5 py-3">{app.name}</td>
|
||||
<td className="px-5 py-3 text-muted-foreground">{app.namespace || '-'}</td>
|
||||
<td className="px-5 py-3">{app.components?.length ?? 0}</td>
|
||||
<td className="px-5 py-3"><Badge tone={getTone(app.status)}>{formatStatus(app.status)}</Badge></td>
|
||||
<td className="px-5 py-3">
|
||||
<Badge tone={getTone(formatStatus(app.status, app.launcherLinked))}>{formatStatus(app.status, app.launcherLinked)}</Badge>
|
||||
</td>
|
||||
<td className="px-5 py-3">
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
@@ -952,7 +954,7 @@ export function AppManagementPage() {
|
||||
['네임스페이스', selectedApp.namespace],
|
||||
['연결 소스', `${linkedComponents}개`],
|
||||
['구성요소', `${totalComponents}개`],
|
||||
['상태', formatStatus(selectedApp.status)]
|
||||
['상태', formatStatus(selectedApp.status, selectedApp.launcherLinked)]
|
||||
].map(([label, value]) => (
|
||||
<div key={label} className="rounded-md border border-border bg-muted/20 p-4">
|
||||
<p className="text-xs text-muted-foreground">{label}</p>
|
||||
@@ -1008,7 +1010,6 @@ export function AppManagementPage() {
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className={cn('size-2.5 shrink-0 rounded-full', running ? statusDotToneClass('green') : component.repository ? statusDotToneClass('blue') : statusDotToneClass('neutral'))} />
|
||||
{component.name}
|
||||
{running ? <Badge tone="green">실행 중</Badge> : null}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-5 py-3 text-muted-foreground">{imageSourceLabel}</td>
|
||||
@@ -1091,7 +1092,6 @@ export function AppManagementPage() {
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className={cn('block size-2.5 shrink-0 rounded-full', stopped ? statusDotToneClass('neutral') : runtimeStatusDotClass(resource.status))} />
|
||||
<span className="text-sm text-foreground">{resource.name}</span>
|
||||
<Badge tone={runtimeStatusTone(resource, stopped)}>{runtimeStatusLabel(resource, stopped)}</Badge>
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-5 py-3 text-foreground">{resource.kind}</td>
|
||||
|
||||
@@ -389,7 +389,6 @@ export function RepositoryManagementPage() {
|
||||
<input
|
||||
value={repoName}
|
||||
onChange={(event) => handleRepoNameChange(event.target.value)}
|
||||
placeholder="lms-fe"
|
||||
className="h-full min-w-0 flex-1 bg-transparent px-3 text-sm outline-none placeholder:text-muted-foreground"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -62,11 +62,19 @@ function splitRepository(repository?: string | null) {
|
||||
return owner && repo ? { owner, repo } : null;
|
||||
}
|
||||
|
||||
function repositoryGitUrl(repository?: string | null) {
|
||||
function httpCloneUrl(repository?: string | null) {
|
||||
const normalized = normalizeRepository(repository);
|
||||
return normalized ? `${giteaBaseUrl.replace(/\/$/, '')}/${normalized}.git` : '-';
|
||||
}
|
||||
|
||||
function sshCloneUrl(repository?: string | null) {
|
||||
const normalized = normalizeRepository(repository);
|
||||
if (!normalized) return '-';
|
||||
const base = giteaBaseUrl.replace(/\/$/, '');
|
||||
const host = base.replace(/^https?:\/\//, '').split('/')[0]?.split(':')[0] || 'localhost';
|
||||
return `ssh://git@${host}:2222/${normalized}.git`;
|
||||
}
|
||||
|
||||
function commitAuthor(commit: GiteaCommit) {
|
||||
return commit.author?.login
|
||||
|| commit.author?.username
|
||||
@@ -321,13 +329,13 @@ export function SourceManagementPage() {
|
||||
const selectedSource = filteredSources.find((source) => source.id === selectedSourceId) ?? filteredSources[0] ?? null;
|
||||
const repository = splitRepository(selectedSource?.repository);
|
||||
const branch = selectedBranch || 'develop';
|
||||
const latestBranchSha = commits.length > 0 ? commits[0]?.sha ?? '' : '';
|
||||
const branchOptions = useMemo(
|
||||
() => [...new Set(['develop', 'release', 'main', selectedSource?.branch].filter((item): item is string => Boolean(item)))],
|
||||
[selectedSource?.branch]
|
||||
);
|
||||
const totalCommitPages = totalCommits === null ? null : Math.max(1, Math.ceil(totalCommits / commitPageSize));
|
||||
const selectedRepositoryGitUrl = repositoryGitUrl(selectedSource?.repository);
|
||||
const selectedHttpCloneUrl = httpCloneUrl(selectedSource?.repository);
|
||||
const selectedSshCloneUrl = sshCloneUrl(selectedSource?.repository);
|
||||
|
||||
const refreshSources = () => {
|
||||
setSourcesLoading(true);
|
||||
@@ -426,11 +434,11 @@ export function SourceManagementPage() {
|
||||
.finally(() => setDiffLoading(false));
|
||||
};
|
||||
|
||||
const copyGitUrl = () => {
|
||||
if (!selectedSource?.repository || selectedRepositoryGitUrl === '-') return;
|
||||
navigator.clipboard?.writeText(selectedRepositoryGitUrl)
|
||||
.then(() => toast({ type: 'success', message: 'Git URL을 복사했습니다.' }))
|
||||
.catch(() => toast({ type: 'error', message: 'Git URL 복사에 실패했습니다.' }));
|
||||
const copyCloneUrl = (value: string, label: string) => {
|
||||
if (!selectedSource?.repository || value === '-') return;
|
||||
navigator.clipboard?.writeText(value)
|
||||
.then(() => toast({ type: 'success', message: `${label}을 복사했습니다.` }))
|
||||
.catch(() => toast({ type: 'error', message: `${label} 복사에 실패했습니다.` }));
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -472,7 +480,7 @@ export function SourceManagementPage() {
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div className="grid gap-3 rounded-md border border-border bg-muted/20 p-3 text-sm md:grid-cols-2">
|
||||
<div className="space-y-3 rounded-md border border-border bg-muted/20 p-3 text-sm">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">조회 브랜치</p>
|
||||
<select
|
||||
@@ -485,27 +493,28 @@ export function SourceManagementPage() {
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="min-w-0 md:col-span-2">
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs text-muted-foreground">저장소</p>
|
||||
<div className="mt-2 rounded-md border border-border bg-background px-3 py-2">
|
||||
<p className="truncate text-sm font-medium">{normalizeRepository(selectedSource?.repository) || '-'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-0 md:col-span-2">
|
||||
<p className="text-xs text-muted-foreground">Git URL</p>
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs text-muted-foreground">HTTP Clone URL</p>
|
||||
<div className="mt-2 flex min-w-0 items-center gap-2 rounded-md border border-border bg-background px-3 py-2">
|
||||
<p className="min-w-0 flex-1 truncate text-sm font-medium" title={selectedRepositoryGitUrl}>{selectedRepositoryGitUrl}</p>
|
||||
<Button variant="ghost" className="size-8 shrink-0 px-0" title="Git URL 복사" aria-label="Git URL 복사" onClick={copyGitUrl} disabled={!selectedSource?.repository}>
|
||||
<p className="min-w-0 flex-1 truncate font-mono text-xs text-muted-foreground" title={selectedHttpCloneUrl}>{selectedHttpCloneUrl}</p>
|
||||
<Button variant="ghost" className="size-8 shrink-0 px-0" title="HTTP Clone URL 복사" aria-label="HTTP Clone URL 복사" onClick={() => copyCloneUrl(selectedHttpCloneUrl, 'HTTP Clone URL')} disabled={!selectedSource?.repository}>
|
||||
<Copy className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-0 md:col-span-2">
|
||||
<p className="text-xs text-muted-foreground">SHA1</p>
|
||||
<div className="mt-2 rounded-md border border-border bg-background px-3 py-2">
|
||||
<p className="truncate font-mono text-sm font-medium" title={latestBranchSha || '-'}>
|
||||
{latestBranchSha || '-'}
|
||||
</p>
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs text-muted-foreground">SSH Clone URL</p>
|
||||
<div className="mt-2 flex min-w-0 items-center gap-2 rounded-md border border-border bg-background px-3 py-2">
|
||||
<p className="min-w-0 flex-1 truncate font-mono text-xs text-muted-foreground" title={selectedSshCloneUrl}>{selectedSshCloneUrl}</p>
|
||||
<Button variant="ghost" className="size-8 shrink-0 px-0" title="SSH Clone URL 복사" aria-label="SSH Clone URL 복사" onClick={() => copyCloneUrl(selectedSshCloneUrl, 'SSH Clone URL')} disabled={!selectedSource?.repository}>
|
||||
<Copy className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user