merge develop into release
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-07-07 09:51:28 +09:00
4 changed files with 41 additions and 19 deletions
+2 -2
View File
@@ -48,7 +48,7 @@ function jobTime(job: LauncherBuildJob) {
return Number.isNaN(value) ? 0 : value; return Number.isNaN(value) ? 0 : value;
} }
const launcherBuildJobPageSize = 5; const launcherBuildJobPageSize = 3;
type BuildTarget = 'launcher-client' | 'launcher-backend'; type BuildTarget = 'launcher-client' | 'launcher-backend';
type BuildRange = 'week' | 'month' | 'all'; type BuildRange = 'week' | 'month' | 'all';
@@ -387,7 +387,7 @@ export function LauncherBuildPage() {
</div> </div>
} }
/> />
<div className="min-h-[638px] space-y-3 p-4"> <div className="min-h-[410px] space-y-3 p-4">
{jobs.length === 0 ? ( {jobs.length === 0 ? (
<div className="flex min-h-44 items-center justify-center text-center text-sm text-muted-foreground"> <div className="flex min-h-44 items-center justify-center text-center text-sm text-muted-foreground">
빌드 작업이 없습니다. 빌드 작업이 없습니다.
@@ -1,4 +1,4 @@
import { MoreHorizontal, Plus, SlidersHorizontal, Trash2 } from 'lucide-react'; import { ChevronLeft, ChevronRight, MoreHorizontal, Plus, SlidersHorizontal, Trash2 } from 'lucide-react';
import { useEffect, useMemo, useState, type FormEvent } from 'react'; import { useEffect, useMemo, useState, type FormEvent } from 'react';
import { Badge, Button, Card, CardHeader, Modal } from '../../components/ui'; import { Badge, Button, Card, CardHeader, Modal } from '../../components/ui';
import { useToast } from '../../components/toast'; import { useToast } from '../../components/toast';
@@ -33,6 +33,8 @@ const emptyForm: FormState = {
isSecret: false isSecret: false
}; };
const settingsPageSize = 10;
function maskValue(setting: LauncherCommonSetting) { function maskValue(setting: LauncherCommonSetting) {
if (!setting.isSecret) return setting.value; if (!setting.isSecret) return setting.value;
if (!setting.value) return '-'; if (!setting.value) return '-';
@@ -61,8 +63,11 @@ export function LauncherCommonSettingsPage() {
const [editing, setEditing] = useState<LauncherCommonSetting | null>(null); const [editing, setEditing] = useState<LauncherCommonSetting | null>(null);
const [form, setForm] = useState<FormState>(emptyForm); const [form, setForm] = useState<FormState>(emptyForm);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [page, setPage] = useState(1);
const loadingVisible = useMinimumLoading(loading); const loadingVisible = useMinimumLoading(loading);
const totalPages = Math.max(1, Math.ceil(settings.length / settingsPageSize));
const pagedSettings = settings.slice((page - 1) * settingsPageSize, page * settingsPageSize);
const summary = useMemo(() => ({ const summary = useMemo(() => ({
total: settings.length, total: settings.length,
endpoint: settings.filter((item) => item.type === 'endpoint').length, endpoint: settings.filter((item) => item.type === 'endpoint').length,
@@ -84,6 +89,10 @@ export function LauncherCommonSettingsPage() {
refresh(); refresh();
}, []); }, []);
useEffect(() => {
setPage((currentPage) => Math.min(currentPage, totalPages));
}, [totalPages]);
const openCreate = () => { const openCreate = () => {
setEditing(null); setEditing(null);
setForm(emptyForm); setForm(emptyForm);
@@ -192,7 +201,7 @@ export function LauncherCommonSettingsPage() {
<td colSpan={5} className="px-5 py-12 text-center text-sm text-muted-foreground">등록된 설정이 없습니다.</td> <td colSpan={5} className="px-5 py-12 text-center text-sm text-muted-foreground">등록된 설정이 없습니다.</td>
</tr> </tr>
) : null} ) : null}
{settings.map((setting) => ( {pagedSettings.map((setting) => (
<tr className="hover:bg-muted/20" key={setting.id}> <tr className="hover:bg-muted/20" key={setting.id}>
<td className="px-5 py-3"> <td className="px-5 py-3">
<p className="inline-flex items-center gap-2 font-medium"> <p className="inline-flex items-center gap-2 font-medium">
@@ -214,6 +223,31 @@ export function LauncherCommonSettingsPage() {
</tbody> </tbody>
</table> </table>
</div> </div>
<div className="flex items-center justify-between gap-4 border-t border-border px-5 py-3 text-sm">
<span className="text-xs text-muted-foreground">
{loading ? '불러오는 중' : `총 ${settings.length}건 · ${page} / ${totalPages}`}
</span>
<div className="flex gap-2">
<Button
variant="outline"
className="h-8 px-3 text-[13px]"
disabled={page <= 1}
onClick={() => setPage((currentPage) => Math.max(1, currentPage - 1))}
>
<ChevronLeft className="size-4" />
이전
</Button>
<Button
variant="outline"
className="h-8 px-3 text-[13px]"
disabled={page >= totalPages}
onClick={() => setPage((currentPage) => Math.min(totalPages, currentPage + 1))}
>
다음
<ChevronRight className="size-4" />
</Button>
</div>
</div>
</Card> </Card>
<Modal <Modal
+3 -9
View File
@@ -1,4 +1,4 @@
import { Copy, ExternalLink, FileText, Link2, RotateCcw } from 'lucide-react'; import { Copy, FileText, Link2, RotateCcw } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { Badge, Button, Card, CardHeader, Modal } from '../../components/ui'; import { Badge, Button, Card, CardHeader, Modal } from '../../components/ui';
import { useToast } from '../../components/toast'; import { useToast } from '../../components/toast';
@@ -226,7 +226,7 @@ function DiffViewer({ diff }: { diff: string }) {
const files = parseDiffFiles(diff); const files = parseDiffFiles(diff);
return ( return (
<div className="h-[72vh] overflow-auto rounded-md border border-border bg-[#080b10] p-3 font-mono text-xs leading-5"> <div className="max-h-[56vh] overflow-auto rounded-md border border-border bg-[#080b10] p-3 font-mono text-xs leading-5">
<div className="space-y-4"> <div className="space-y-4">
{files.map((file) => ( {files.map((file) => (
<section key={file.id} className="min-w-max overflow-hidden rounded-md border border-slate-700/80 bg-[#0b0f14]"> <section key={file.id} className="min-w-max overflow-hidden rounded-md border border-slate-700/80 bg-[#0b0f14]">
@@ -510,12 +510,6 @@ export function LauncherSourcePage() {
</Button> </Button>
</div> </div>
</div> </div>
{activeForm.repository ? (
<a className="inline-flex items-center gap-1.5 text-xs text-primary" href={`${giteaBaseUrl.replace(/\/$/, '')}/${normalizeRepository(activeForm.repository, activeTargetMeta.defaultOrg)}`} target="_blank" rel="noreferrer">
저장소 열기
<ExternalLink className="size-3.5" />
</a>
) : null}
</div> </div>
</Card> </Card>
</div> </div>
@@ -618,7 +612,7 @@ export function LauncherSourcePage() {
title="커밋 내역 상세" title="커밋 내역 상세"
onClose={() => setDiffTarget(null)} onClose={() => setDiffTarget(null)}
className="max-w-[min(98vw,1800px)]" className="max-w-[min(98vw,1800px)]"
bodyClassName="max-h-[84vh] px-4 py-4" bodyClassName="max-h-[70vh] px-4 py-4"
footer={<Button variant="outline" onClick={() => setDiffTarget(null)}>닫기</Button>} footer={<Button variant="outline" onClick={() => setDiffTarget(null)}>닫기</Button>}
> >
{diffTarget ? ( {diffTarget ? (
@@ -267,12 +267,6 @@ export function LauncherVersionsPage() {
<span>커밋 <b className="font-mono text-foreground">{latestBackendBuild?.commitSha || '-'}</b></span> <span>커밋 <b className="font-mono text-foreground">{latestBackendBuild?.commitSha || '-'}</b></span>
</div> </div>
</div> </div>
<div className="flex justify-end">
<Button onClick={() => setDeployOpen(true)} disabled={!latestBackendBuild?.downloadUrl || deploying}>
<Play className="size-4" />
백엔드 배포
</Button>
</div>
</div> </div>
</Card> </Card>
<Card> <Card>