show notice creators with admin avatars
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-07-10 11:11:25 +09:00
parent 2032b696e5
commit 21c65709b8
3 changed files with 37 additions and 6 deletions
+5 -5
View File
@@ -66,20 +66,20 @@ function BuildWorker({ job }: { job: LauncherBuildJob }) {
const initial = name.trim().charAt(0).toUpperCase() || '?';
return (
<span className="inline-flex min-w-0 items-center gap-2">
<span className="inline-flex h-4 min-w-0 items-center gap-1.5 align-middle">
{avatarUrl && !failed ? (
<img
src={avatarUrl}
alt=""
className="size-6 shrink-0 rounded-full border border-border bg-muted object-cover"
className="size-4 shrink-0 rounded-full border border-border bg-muted object-cover"
onError={() => setFailed(true)}
/>
) : (
<span className="inline-flex size-6 shrink-0 items-center justify-center rounded-full border border-border bg-muted text-[11px] font-semibold text-muted-foreground">
<span className="inline-flex size-4 shrink-0 items-center justify-center rounded-full border border-border bg-muted text-[9px] font-semibold leading-none text-muted-foreground">
{initial}
</span>
)}
<b className="truncate text-foreground">{name}</b>
<b className="truncate leading-none text-foreground">{name}</b>
</span>
);
}
@@ -441,7 +441,7 @@ export function LauncherBuildPage() {
<div className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
<span>브랜치 <b className="text-foreground">{job.branch}</b></span>
<span>커밋 <b className="font-mono text-foreground">{job.commitSha || '-'}</b></span>
<span className="inline-flex min-w-0 items-center gap-2">작업자 <BuildWorker job={job} /></span>
<span className="inline-flex h-4 min-w-0 items-center gap-1.5 align-middle">작업자 <BuildWorker job={job} /></span>
<span>생성 <b className="text-foreground">{formatDate(job.createdAt)}</b></span>
</div>
</div>
+29 -1
View File
@@ -73,6 +73,34 @@ function exposureCell(row: LauncherNotice) {
);
}
function NoticeCreator({ notice }: { notice: LauncherNotice }) {
const [failed, setFailed] = useState(false);
const name = notice.createdByName || notice.createdByEmail || 'admin';
const avatarUrl = notice.createdByAvatarUrl || '';
const initial = name.trim().charAt(0).toUpperCase() || 'A';
return (
<div className="flex min-w-0 items-center gap-2">
{avatarUrl && !failed ? (
<img
src={avatarUrl}
alt=""
className="size-7 shrink-0 rounded-full border border-border bg-muted object-cover"
onError={() => setFailed(true)}
/>
) : (
<span className="inline-flex size-7 shrink-0 items-center justify-center rounded-full border border-border bg-muted text-xs font-semibold text-muted-foreground">
{initial}
</span>
)}
<div className="min-w-0">
<p className="truncate text-sm font-medium text-foreground">{name}</p>
<p className="mt-0.5 whitespace-nowrap text-xs text-muted-foreground">{formatNoticeDateTime(notice.createdAt)}</p>
</div>
</div>
);
}
function toForm(notice: LauncherNotice): NoticeForm {
return {
appKey: notice.appKey,
@@ -246,7 +274,7 @@ export function NoticesPage() {
},
{ key: 'appKey', header: '대상 앱', cell: (row) => <StatusBadge status={row.appName} tone={isLauncherNoticeKey(row.appKey) ? 'violet' : 'blue'} /> },
{ key: 'exposure', header: '노출 일시', className: 'w-48 px-5 py-3 font-medium', cell: exposureCell },
{ key: 'createdAt', header: '생성일시', cell: (row) => <span className="text-muted-foreground">{formatNoticeDateTime(row.createdAt)}</span> },
{ key: 'createdAt', header: '생성', className: 'w-48 px-5 py-3', cell: (row) => <NoticeCreator notice={row} /> },
{
key: 'actions',
header: '',
+3
View File
@@ -79,6 +79,9 @@ export type LauncherNotice = {
endsAt?: string | null;
priority: number;
status?: string;
createdByName?: string | null;
createdByEmail?: string | null;
createdByAvatarUrl?: string | null;
createdAt: string;
updatedAt?: string;
};