fix: use admin R2 avatars for operator activity..
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
+2
-1
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
MonitorPlay,
|
||||
Bell,
|
||||
BookOpen,
|
||||
Boxes,
|
||||
ChartNoAxesColumnIncreasing,
|
||||
FileKey,
|
||||
@@ -99,7 +100,7 @@ export const navGroups = [
|
||||
{ label: '라이센스', icon: FileKey },
|
||||
{ label: '공지사항', icon: Megaphone },
|
||||
{ label: '약관', icon: ScrollText },
|
||||
{ label: '매뉴얼', icon: ScrollText }
|
||||
{ label: '매뉴얼', icon: BookOpen }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -19,6 +19,27 @@ type ManualForm = Pick<ManualDocument, 'appKey' | 'title' | 'body' | 'isPublishe
|
||||
|
||||
const emptyForm: ManualForm = { appKey: '', title: '', body: '', isPublished: true, sortOrder: 0 };
|
||||
|
||||
function ManualCreator({ document }: { document: ManualDocument }) {
|
||||
const [failed, setFailed] = useState(false);
|
||||
const name = document.createdByName || document.createdByEmail || '-';
|
||||
const avatarUrl = document.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">{formatDateTime(document.createdAt)}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ManualsPage() {
|
||||
const { toast } = useToast();
|
||||
const [documents, setDocuments] = useState<ManualDocument[]>([]);
|
||||
@@ -92,7 +113,7 @@ export function ManualsPage() {
|
||||
{ key: 'body', header: '본문', flexible: true, className: 'px-5 py-3', cell: (row) => <p className="truncate">{row.body}</p> },
|
||||
{ key: 'published', header: '공개', width: 100, className: 'px-5 py-3', cell: (row) => <StatusBadge status={row.isPublished ? '공개' : '비공개'} tone={row.isPublished ? 'green' : 'neutral'} /> },
|
||||
{ key: 'sortOrder', header: '순서', width: 80, className: 'px-5 py-3 text-center', cell: (row) => row.sortOrder },
|
||||
{ key: 'createdAt', header: '등록일', width: 160, className: 'px-5 py-3 text-xs text-muted-foreground', cell: (row) => formatDateTime(row.createdAt) },
|
||||
{ key: 'createdAt', header: '생성', width: 190, className: 'px-5 py-3', cell: (row) => <ManualCreator document={row} /> },
|
||||
{ key: 'actions', header: '', className: 'px-5 py-3', cell: (row) => <div className="relative flex justify-end"><Button variant="ghost" className="size-8 px-0" title="메뉴" aria-label="메뉴" onClick={(event) => { event.stopPropagation(); const rect = event.currentTarget.getBoundingClientRect(); setMenu((previous) => previous?.id === row.id ? null : { id: row.id, x: rect.right, y: rect.bottom + 6 }); }}><MoreHorizontal className="size-4" /></Button></div> },
|
||||
], []);
|
||||
|
||||
|
||||
@@ -134,6 +134,10 @@ export type ManualDocument = {
|
||||
body: string;
|
||||
isPublished: boolean;
|
||||
sortOrder: number;
|
||||
createdById?: string | null;
|
||||
createdByName?: string | null;
|
||||
createdByEmail?: string | null;
|
||||
createdByAvatarUrl?: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user