add app monitoring charts and runtime metrics
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-07-09 13:09:35 +09:00
parent aa483e8b9c
commit 40ea8aaa7a
5 changed files with 730 additions and 12 deletions
+30 -10
View File
@@ -21,6 +21,7 @@ type NoticeForm = {
title: string;
body: string;
linkUrl: string;
exposureEnabled: boolean;
startsAt: string;
endsAt: string;
priority: string;
@@ -32,6 +33,7 @@ const emptyForm: NoticeForm = {
title: '',
body: '',
linkUrl: '',
exposureEnabled: false,
startsAt: '',
endsAt: '',
priority: '0',
@@ -77,6 +79,7 @@ function toForm(notice: LauncherNotice): NoticeForm {
title: notice.title,
body: notice.body,
linkUrl: notice.linkUrl ?? '',
exposureEnabled: Boolean(notice.startsAt || notice.endsAt),
startsAt: toDateTimeInputValue(notice.startsAt),
endsAt: toDateTimeInputValue(notice.endsAt),
priority: String(notice.priority ?? 0),
@@ -181,8 +184,8 @@ export function NoticesPage() {
title: form.title.trim(),
body: form.body.trim(),
linkUrl: form.linkUrl.trim(),
startsAt: toApiDateTime(form.startsAt),
endsAt: toApiDateTime(form.endsAt),
startsAt: form.exposureEnabled ? toApiDateTime(form.startsAt) : undefined,
endsAt: form.exposureEnabled ? toApiDateTime(form.endsAt) : undefined,
priority: Number(form.priority) || 0,
isActive: form.isActive
});
@@ -382,15 +385,32 @@ export function NoticesPage() {
<span className="text-xs font-medium text-muted-foreground">링크주소</span>
<input className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm text-foreground outline-none focus:ring-2 focus:ring-ring" value={form.linkUrl} onChange={(event) => setForm((prev) => ({ ...prev, linkUrl: event.target.value }))} />
</label>
<div className="grid gap-4 md:grid-cols-2">
<label className="block">
<span className="text-xs font-medium text-muted-foreground">노출 시작 일시</span>
<input type="datetime-local" className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm text-foreground outline-none focus:ring-2 focus:ring-ring" value={form.startsAt} onChange={(event) => setForm((prev) => ({ ...prev, startsAt: event.target.value }))} />
</label>
<label className="block">
<span className="text-xs font-medium text-muted-foreground">노출 종료 일시</span>
<input type="datetime-local" className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm text-foreground outline-none focus:ring-2 focus:ring-ring" value={form.endsAt} onChange={(event) => setForm((prev) => ({ ...prev, endsAt: event.target.value }))} />
<div className="rounded-md border border-border bg-muted/20 p-3">
<label className="inline-flex items-center gap-2 text-sm font-medium text-foreground">
<input
type="checkbox"
checked={form.exposureEnabled}
onChange={(event) => setForm((prev) => ({
...prev,
exposureEnabled: event.target.checked,
startsAt: event.target.checked ? prev.startsAt : '',
endsAt: event.target.checked ? prev.endsAt : ''
}))}
/>
노출기간 설정
</label>
{form.exposureEnabled ? (
<div className="mt-3 grid gap-4 md:grid-cols-2">
<label className="block">
<span className="text-xs font-medium text-muted-foreground">노출 시작 일시</span>
<input type="datetime-local" className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm text-foreground outline-none focus:ring-2 focus:ring-ring" value={form.startsAt} onChange={(event) => setForm((prev) => ({ ...prev, startsAt: event.target.value }))} />
</label>
<label className="block">
<span className="text-xs font-medium text-muted-foreground">노출 종료 일시</span>
<input type="datetime-local" className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm text-foreground outline-none focus:ring-2 focus:ring-ring" value={form.endsAt} onChange={(event) => setForm((prev) => ({ ...prev, endsAt: event.target.value }))} />
</label>
</div>
) : null}
</div>
</form>
</Modal>